packages feed

git-annex 6.20170214 → 6.20170301

raw patch · 89 files changed

+1154/−538 lines, 89 filesdep −cryptohash

Dependencies removed: cryptohash

Files

Annex/Common.hs view
@@ -2,7 +2,7 @@  import Common as X import Types as X-import Types.Key as X+import Key as X import Types.UUID as X import Annex as X (gitRepo, inRepo, fromRepo, calcRepo) import Annex.Locations as X
Annex/Concurrent.hs view
@@ -56,14 +56,16 @@  - Also closes various handles in it. -} mergeState :: AnnexState -> Annex () mergeState st = do-	st' <- liftIO $ snd <$> run st closehandles+	st' <- liftIO $ snd <$> run st stopCoProcesses 	forM_ (M.toList $ Annex.cleanup st') $ 		uncurry addCleanup 	Annex.Queue.mergeFrom st' 	changeState $ \s -> s { errcounter = errcounter s + errcounter st' }-  where-	closehandles = do-		catFileStop-		checkAttrStop-		hashObjectStop-		checkIgnoreStop++{- Stops all long-running git query processes. -}+stopCoProcesses :: Annex ()+stopCoProcesses = do+	catFileStop+	checkAttrStop+	hashObjectStop+	checkIgnoreStop
Annex/Content.hs view
@@ -1,6 +1,6 @@ {- git-annex file content managing  -- - Copyright 2010-2015 Joey Hess <id@joeyh.name>+ - Copyright 2010-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -80,6 +80,7 @@ import qualified Backend import qualified Database.Keys import Types.NumCopies+import Types.Key import Annex.UUID import Annex.InodeSentinal import Utility.InodeCache@@ -307,10 +308,12 @@ 	(ok, verification) <- action tmpfile 	if ok 		then ifM (verifyKeyContent v verification key tmpfile)-			( do-				moveAnnex key tmpfile-				logStatus key InfoPresent-				return True+			( ifM (moveAnnex key tmpfile)+				( do+					logStatus key InfoPresent+					return True+				, return False+				) 			, do 				warning "verification of content failed" 				liftIO $ nukeFile tmpfile@@ -341,7 +344,7 @@ 		Just size -> do 			size' <- liftIO $ catchDefaultIO 0 $ getFileSize f 			return (size' == size)-	verifycontent = case Types.Backend.verifyKeyContent =<< Backend.maybeLookupBackendName (keyBackendName k) of+	verifycontent = case Types.Backend.verifyKeyContent =<< Backend.maybeLookupBackendVariety (keyVariety k) of 		Nothing -> return True 		Just verifier -> verifier k f @@ -465,9 +468,18 @@  - key, and one of them will probably get deleted later. So, adding the  - check here would only raise expectations that git-annex cannot truely  - meet.+ -+ - May return false, when a particular variety of key is not being+ - accepted into the repository. Will display a warning message in this+ - case. May also throw exceptions in some cases.  -}-moveAnnex :: Key -> FilePath -> Annex ()-moveAnnex key src = withObjectLoc key storeobject storedirect+moveAnnex :: Key -> FilePath -> Annex Bool+moveAnnex key src = ifM (checkSecureHashes key)+	( do+		withObjectLoc key storeobject storedirect+		return True+	, return False+	)   where 	storeobject dest = ifM (liftIO $ doesFileExist dest) 		( alreadyhave@@ -509,6 +521,16 @@ 	 	alreadyhave = liftIO $ removeFile src +checkSecureHashes :: Key -> Annex Bool+checkSecureHashes key+	| cryptographicallySecure (keyVariety key) = return True+	| otherwise = ifM (annexSecureHashesOnly <$> Annex.getGitConfig)+		( do+			warning $ "annex.securehashesonly blocked adding " ++ formatKeyVariety (keyVariety key) ++ " key to annex objects"+			return False+		, return True+		)+ populatePointerFile :: Key -> FilePath -> FilePath -> Annex () populatePointerFile k obj f = go =<< liftIO (isPointerFile f)   where@@ -526,9 +548,12 @@ {- Populates the annex object file by hard linking or copying a source  - file to it. -} linkToAnnex :: Key -> FilePath -> Maybe InodeCache -> Annex LinkAnnexResult-linkToAnnex key src srcic = do-	dest <- calcRepo (gitAnnexLocation key)-	modifyContent dest $ linkAnnex To key src srcic dest Nothing+linkToAnnex key src srcic = ifM (checkSecureHashes key)+	( do+		dest <- calcRepo (gitAnnexLocation key)+		modifyContent dest $ linkAnnex To key src srcic dest Nothing+	, return LinkAnnexFailed+	)  {- Makes a destination file be a link or copy from the annex object. -} linkFromAnnex :: Key -> FilePath -> Maybe FileMode -> Annex LinkAnnexResult
Annex/DirHashes.hs view
@@ -23,7 +23,7 @@ import Data.Default  import Common-import Types.Key+import Key import Types.GitConfig import Types.Difference import Utility.FileSystemEncoding
Annex/Direct.hs view
@@ -383,8 +383,11 @@ removeDirect k f = do 	void $ removeAssociatedFileUnchecked k f 	unlessM (inAnnex k) $+		-- If moveAnnex rejects the content of the key,+		-- treat that the same as its content having changed. 		ifM (goodContent k f)-			( moveAnnex k f+			( unlessM (moveAnnex k f) $+				logStatus k InfoMissing 			, logStatus k InfoMissing 			) 	liftIO $ do
Annex/FileMatcher.hs view
@@ -117,6 +117,7 @@ 		, SimpleToken "groupwanted" (call matchgroupwanted) 		, SimpleToken "present" (simply $ limitPresent mu) 		, SimpleToken "inpreferreddir" (simply $ limitInDir preferreddir)+		, SimpleToken "securehash" (simply limitSecureHash) 		, ValueToken "copies" (usev limitCopies) 		, ValueToken "lackingcopies" (usev $ limitLackingCopies False) 		, ValueToken "approxlackingcopies" (usev $ limitLackingCopies True)
Annex/Hook.hs view
@@ -4,7 +4,7 @@  - not change, otherwise removing old hooks using an old version of  - the script would fail.  -- - Copyright 2013-2014 Joey Hess <id@joeyh.name>+ - Copyright 2013-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -21,6 +21,9 @@  preCommitHook :: Git.Hook preCommitHook = Git.Hook "pre-commit" (mkHookScript "git annex pre-commit .")++postReceiveHook :: Git.Hook+postReceiveHook = Git.Hook "post-receive" (mkHookScript "git annex post-receive")  preCommitAnnexHook :: Git.Hook preCommitAnnexHook = Git.Hook "pre-commit-annex" ""
Annex/Ingest.hs view
@@ -172,10 +172,13 @@ 	go _ _ _ = failure "failed to generate a key"  	golocked key mcache s = do-		catchNonAsync (moveAnnex key $ contentLocation source)-			(restoreFile (keyFilename source) key)-		populateAssociatedFiles key source-		success key mcache s+		v <- tryNonAsync (moveAnnex key $ contentLocation source)+		case v of+			Right True -> do+				populateAssociatedFiles key source+				success key mcache s		+			Right False -> giveup "failed to add content to annex"+			Left e -> restoreFile (keyFilename source) key e  	gounlocked key (Just cache) s = do 		-- Remove temp directory hard link first because@@ -352,8 +355,11 @@  {- Adds a file to the work tree for the key, and stages it in the index.  - The content of the key may be provided in a temp file, which will be- - moved into place. -}-addAnnexedFile :: FilePath -> Key -> Maybe FilePath -> Annex ()+ - moved into place.+ -+ - When the content of the key is not accepted into the annex, returns False.+ -}+addAnnexedFile :: FilePath -> Key -> Maybe FilePath -> Annex Bool addAnnexedFile file key mtmp = ifM (addUnlocked <&&> not <$> isDirect) 	( do 		mode <- maybe@@ -363,12 +369,13 @@ 		stagePointerFile file mode =<< hashPointerFile key 		Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file) 		case mtmp of-			Just tmp -> do-				moveAnnex key tmp-				linkunlocked mode+			Just tmp -> ifM (moveAnnex key tmp)+				( linkunlocked mode >> return True+				, writepointer mode >> return False+				) 			Nothing -> ifM (inAnnex key)-				( linkunlocked mode-				, liftIO $ writePointerFile file key mode+				( linkunlocked mode >> return True+				, writepointer mode >> return True 				) 	, do 		addLink file key Nothing@@ -381,7 +388,7 @@ 				whenM isDirect $ 					Annex.Queue.flush 				moveAnnex key tmp-			Nothing -> return ()+			Nothing -> return True 	)   where 	linkunlocked mode = do@@ -390,3 +397,4 @@ 			LinkAnnexFailed -> liftIO $ 				writePointerFile file key mode 			_ -> return ()+	writepointer mode = liftIO $ writePointerFile file key mode
Annex/Init.hs view
@@ -1,6 +1,6 @@ {- git-annex repository initialization  -- - Copyright 2011-2016 Joey Hess <id@joeyh.name>+ - Copyright 2011-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -26,6 +26,7 @@ import qualified Annex.Branch import Logs.UUID import Logs.Trust.Basic+import Logs.Config import Types.TrustLevel import Annex.Version import Annex.Difference@@ -83,8 +84,9 @@ 	checkLockSupport 	checkFifoSupport 	checkCrippledFileSystem-	unlessM isBareRepo $+	unlessM isBareRepo $ do 		hookWrite preCommitHook+		hookWrite postReceiveHook 	setDifferences 	unlessM (isJust <$> getVersion) $ 		setVersion (fromMaybe defaultVersion mversion)@@ -109,11 +111,13 @@ 				, unlessM isBareRepo 					switchHEADBack 				)+	propigateSecureHashesOnly 	createInodeSentinalFile False  uninitialize :: Annex () uninitialize = do 	hookUnWrite preCommitHook+	hookUnWrite postReceiveHook 	removeRepoUUID 	removeVersion @@ -255,3 +259,12 @@ 	u <- getUUID 	trustSet u UnTrusted 	setConfig (annexConfig "hardlink") (Git.Config.boolConfig True)++{- Propigate annex.securehashesonly from then global config to local+ - config. This makes a clone inherit a parent's setting, but once+ - a repository has a local setting, changes to the global config won't+ - affect it. -}+propigateSecureHashesOnly :: Annex ()+propigateSecureHashesOnly =+	maybe noop (setConfig (ConfigKey "annex.securehashesonly"))+		=<< getGlobalConfig "annex.securehashesonly"
Annex/Locations.hs view
@@ -77,6 +77,7 @@ import Data.Default  import Common+import Key import Types.Key import Types.UUID import Types.GitConfig@@ -478,7 +479,7 @@ 	| null s = True -- it's not legal for a key to have no keyName 	| otherwise= Just k == fileKey (keyFile k)   where-	k = stubKey { keyName = s, keyBackendName = "test" }+	k = stubKey { keyName = s, keyVariety = OtherKey "test" }  {- A location to store a key on a special remote that uses a filesystem.  - A directory hash is used, to protect against filesystems that dislike
Annex/Perms.hs view
@@ -24,7 +24,7 @@  import Annex.Common import Utility.FileMode-import Git.SharedRepository+import Git.ConfigTypes import qualified Annex import Config 
Annex/Ssh.hs view
@@ -1,6 +1,6 @@ {- git-annex ssh interface, with connection caching  -- - Copyright 2012-2015 Joey Hess <id@joeyh.name>+ - Copyright 2012-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -8,6 +8,7 @@ {-# LANGUAGE CPP #-}  module Annex.Ssh (+	ConsumeStdin(..), 	sshOptions, 	sshCacheDir, 	sshReadPort,@@ -41,10 +42,15 @@ import Annex.LockPool #endif +{- Some ssh commands are fed stdin on a pipe and so should be allowed to+ - consume it. But ssh commands that are not piped stdin should generally+ - not be allowed to consume the process's stdin. -}+data ConsumeStdin = ConsumeStdin | NoConsumeStdin+ {- Generates parameters to ssh to a given host (or user@host) on a given  - port. This includes connection caching parameters, and any ssh-options. -}-sshOptions :: (String, Maybe Integer) -> RemoteGitConfig -> [CommandParam] -> Annex [CommandParam]-sshOptions (host, port) gc opts = go =<< sshCachingInfo (host, port)+sshOptions :: ConsumeStdin -> (String, Maybe Integer) -> RemoteGitConfig -> [CommandParam] -> Annex [CommandParam]+sshOptions cs (host, port) gc opts = go =<< sshCachingInfo (host, port)   where 	go (Nothing, params) = ret params 	go (Just socketfile, params) = do@@ -55,6 +61,9 @@ 		, map Param (remoteAnnexSshOptions gc) 		, opts 		, portParams port+		, case cs of+			ConsumeStdin -> []+			NoConsumeStdin -> [Param "-n"] 		, [Param "-T"] 		] @@ -249,7 +258,8 @@  - options. (The options are separated by newlines.)  -  - This is a workaround for GIT_SSH not being able to contain- - additional parameters to pass to ssh. -}+ - additional parameters to pass to ssh. (GIT_SSH_COMMAND can,+ - but is not supported by older versions of git.) -} sshOptionsEnv :: String sshOptionsEnv = "GIT_ANNEX_SSHOPTION" 
Annex/Transfer.hs view
@@ -1,6 +1,6 @@ {- git-annex transfers  -- - Copyright 2012-2016 Joey Hess <id@joeyh.name>+ - Copyright 2012-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -26,6 +26,7 @@ import Annex.Perms import Utility.Metered import Annex.LockPool+import Types.Key import Types.Remote (Verification(..)) import qualified Types.Remote as Remote import Types.Concurrency@@ -87,7 +88,7 @@ alwaysRunTransfer = runTransfer' True  runTransfer' :: Observable v => Bool -> Transfer -> Maybe FilePath -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v-runTransfer' ignorelock t file shouldretry transferaction = do+runTransfer' ignorelock t file shouldretry transferaction = checkSecureHashes t $ do 	info <- liftIO $ startTransferInfo file 	(meter, tfile, metervar) <- mkProgressUpdater t info 	mode <- annexFileMode@@ -166,6 +167,30 @@ 		| otherwise = do 			f <- fromRepo $ gitAnnexTmpObjectLocation (transferKey t) 			liftIO $ catchDefaultIO 0 $ getFileSize f++{- Avoid download and upload of keys with insecure content when+ - annex.securehashesonly is configured.+ -+ - This is not a security check. Even if this let the content be+ - downloaded, the actual security checks would prevent the content from+ - being added to the repository. The only reason this is done here is to+ - avoid transferring content that's going to be rejected anyway.+ -+ - We assume that, if annex.securehashesonly is set and the local repo+ - still contains content using an insecure hash, remotes will likewise+ - tend to be configured to reject it, so Upload is also prevented.+ -}+checkSecureHashes :: Observable v => Transfer -> Annex v -> Annex v+checkSecureHashes t a+	| cryptographicallySecure variety = a+	| otherwise = ifM (annexSecureHashesOnly <$> Annex.getGitConfig)+		( do+			warning $ "annex.securehashesonly blocked transfer of " ++ formatKeyVariety variety ++ " key"+			return observeFailure+		, a+		)+  where+	variety = keyVariety (transferKey t)  type RetryDecider = TransferInfo -> TransferInfo -> Bool 
+ Annex/UpdateInstead.hs view
@@ -0,0 +1,27 @@+{- git-annex UpdateIntead emulation+ -+ - Copyright 2017 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Annex.UpdateInstead where++import qualified Annex+import Annex.Common+import Config+import Annex.Version+import Annex.AdjustedBranch+import Git.Branch+import Git.ConfigTypes++{- receive.denyCurrentBranch=updateInstead does not work in direct mode+ - repositories or when an adjusted branch is checked out, so must be+ - emulated. -}+needUpdateInsteadEmulation :: Annex Bool+needUpdateInsteadEmulation = updateinsteadset <&&> (isDirect <||> isadjusted)+  where+	updateinsteadset = (== UpdateInstead) . receiveDenyCurrentBranch+		<$> Annex.getGitConfig+	isadjusted = versionSupportsUnlockedPointers+		<&&> (maybe False (isJust . getAdjustment) <$> inRepo Git.Branch.current)
Assistant/Threads/SanityChecker.hs view
@@ -39,6 +39,7 @@ import Assistant.Unused import Logs.Unused import Types.Transfer+import Types.Key import Annex.Path import qualified Annex #ifdef WITH_WEBAPP@@ -308,7 +309,7 @@ 	cleanjunk check f = case fileKey (takeFileName f) of 		Nothing -> cleanOld check f 		Just k-			| "GPGHMAC" `isPrefixOf` keyBackendName k ->+			| "GPGHMAC" `isPrefixOf` formatKeyVariety (keyVariety k) -> 				cleanOld check f 			| otherwise -> noop 
Assistant/Upgrade.hs view
@@ -115,7 +115,7 @@ 	| otherwise = cleanup   where 	k = distributionKey d-	fsckit f = case Backend.maybeLookupBackendName (Types.Key.keyBackendName k) of+	fsckit f = case Backend.maybeLookupBackendVariety (Types.Key.keyVariety k) of 		Nothing -> return $ Just f 		Just b -> case Types.Backend.verifyKeyContent b of 			Nothing -> return $ Just f@@ -324,7 +324,7 @@ 		ifM (Url.downloadQuiet distributionInfoUrl infof uo 			<&&> Url.downloadQuiet distributionInfoSigUrl sigf uo 			<&&> verifyDistributionSig gpgcmd sigf)-			( readish <$> readFileStrict infof+			( parseInfoFile <$> readFileStrict infof 			, return Nothing 			) 
Assistant/WebApp/Configurators/Ssh.hs view
@@ -379,7 +379,7 @@ 	geti f = maybe "" T.unpack (f sshinput)  	go extraopts environ = processTranscript' -		(askPass environ) "ssh" (extraopts ++ opts)+		(askPass environ (proc "ssh" (extraopts ++ opts))) 		-- Always provide stdin, even when empty. 		(Just (fromMaybe "" input)) @@ -746,7 +746,9 @@ 	probeuuid sshdata = do 		r <- inRepo $ Git.Construct.fromRemoteLocation (fromJust $ sshRepoUrl sshdata) 		getUncachedUUID . either (const r) fst <$>-			Remote.Helper.Ssh.onRemote r (Git.Config.fromPipe r, return (Left $ error "configlist failed")) "configlist" [] []+			Remote.Helper.Ssh.onRemote NoConsumeStdin r+				(Git.Config.fromPipe r, return (Left $ error "configlist failed"))+				"configlist" [] [] 	verifysshworks sshdata = inRepo $ Git.Command.runBool 		[ Param "send-pack" 		, Param (fromJust $ sshRepoUrl sshdata)
Backend.hs view
@@ -1,6 +1,6 @@ {- git-annex key/value backends  -- - Copyright 2010-2014 Joey Hess <id@joeyh.name>+ - Copyright 2010-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -11,14 +11,15 @@ 	genKey, 	getBackend, 	chooseBackend,-	lookupBackendName,-	maybeLookupBackendName,+	lookupBackendVariety,+	maybeLookupBackendVariety, 	isStableKey, ) where  import Annex.Common import qualified Annex import Annex.CheckAttr+import Types.Key import Types.KeySource import qualified Types.Backend as B @@ -42,14 +43,15 @@ 			f <- Annex.getState Annex.forcebackend 			case f of 				Just name | not (null name) ->-					return [lookupBackendName name]+					return [lookupname name] 				_ -> do 					l' <- gen . annexBackends <$> Annex.getGitConfig 					Annex.changeState $ \s -> s { Annex.backends = l' } 					return l'   where 	gen [] = list-	gen l = map lookupBackendName l+	gen ns = map lookupname ns+	lookupname = lookupBackendVariety . parseKeyVariety  {- Generates a key for a file, trying each backend in turn until one  - accepts it. -}@@ -73,33 +75,33 @@ 		| otherwise = c  getBackend :: FilePath -> Key -> Annex (Maybe Backend)-getBackend file k = let bname = keyBackendName k in-	case maybeLookupBackendName bname of-		Just backend -> return $ Just backend-		Nothing -> do-			warning $ "skipping " ++ file ++ " (unknown backend " ++ bname ++ ")"-			return Nothing+getBackend file k = case maybeLookupBackendVariety (keyVariety k) of+	Just backend -> return $ Just backend+	Nothing -> do+		warning $ "skipping " ++ file ++ " (unknown backend " ++ formatKeyVariety (keyVariety k) ++ ")"+		return Nothing  {- Looks up the backend that should be used for a file.  - That can be configured on a per-file basis in the gitattributes file. -} chooseBackend :: FilePath -> Annex (Maybe Backend) chooseBackend f = Annex.getState Annex.forcebackend >>= go   where-	go Nothing =  maybeLookupBackendName <$> checkAttr "annex.backend" f+	go Nothing =  maybeLookupBackendVariety . parseKeyVariety+		<$> checkAttr "annex.backend" f 	go (Just _) = Just . Prelude.head <$> orderedList -{- Looks up a backend by name. May fail if unknown. -}-lookupBackendName :: String -> Backend-lookupBackendName s = fromMaybe unknown $ maybeLookupBackendName s+{- Looks up a backend by variety. May fail if unsupported or disabled. -}+lookupBackendVariety :: KeyVariety -> Backend+lookupBackendVariety v = fromMaybe unknown $ maybeLookupBackendVariety v   where-	unknown = error $ "unknown backend " ++ s+	unknown = error $ "unknown backend " ++ formatKeyVariety v -maybeLookupBackendName :: String -> Maybe Backend-maybeLookupBackendName s = M.lookup s nameMap+maybeLookupBackendVariety :: KeyVariety -> Maybe Backend+maybeLookupBackendVariety v = M.lookup v varietyMap -nameMap :: M.Map String Backend-nameMap = M.fromList $ zip (map B.name list) list+varietyMap :: M.Map KeyVariety Backend+varietyMap = M.fromList $ zip (map B.backendVariety list) list  isStableKey :: Key -> Bool isStableKey k = maybe False (`B.isStableKey` k) -	(maybeLookupBackendName (keyBackendName k))+	(maybeLookupBackendVariety (keyVariety k))
Backend/Hash.hs view
@@ -1,12 +1,10 @@ {- git-annex hashing backends  -- - Copyright 2011-2015 Joey Hess <id@joeyh.name>+ - Copyright 2011-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -} -{-# LANGUAGE CPP #-}- module Backend.Hash ( 	backends, 	testKeyBackend,@@ -14,6 +12,7 @@  import Annex.Common import qualified Annex+import Types.Key import Types.Backend import Types.KeySource import Utility.Hash@@ -29,17 +28,14 @@ 	| SHA2Hash HashSize 	| SHA3Hash HashSize 	| SkeinHash HashSize-type HashSize = Int  {- Order is slightly significant; want SHA256 first, and more general  - sizes earlier. -} hashes :: [Hash] hashes = concat -	[ map SHA2Hash [256, 512, 224, 384]-#ifdef WITH_CRYPTONITE-	, map SHA3Hash [256, 512, 224, 384]-#endif-	, map SkeinHash [256, 512]+	[ map (SHA2Hash . HashSize) [256, 512, 224, 384]+	, map (SHA3Hash . HashSize) [256, 512, 224, 384]+	, map (SkeinHash . HashSize) [256, 512] 	, [SHA1Hash] 	, [MD5Hash] 	]@@ -50,7 +46,7 @@  genBackend :: Hash -> Backend genBackend hash = Backend-	{ name = hashName hash+	{ backendVariety = hashKeyVariety hash (HasExt False) 	, getKey = keyValue hash 	, verifyKeyContent = Just $ checkKeyChecksum hash 	, canUpgradeKey = Just needsUpgrade@@ -60,19 +56,16 @@  genBackendE :: Hash -> Backend genBackendE hash = (genBackend hash)-	{ name = hashNameE hash+	{ backendVariety = hashKeyVariety hash (HasExt True) 	, getKey = keyValueE hash 	} -hashName :: Hash -> String-hashName MD5Hash = "MD5"-hashName SHA1Hash = "SHA1"-hashName (SHA2Hash size) = "SHA" ++ show size-hashName (SHA3Hash size) = "SHA3_" ++ show size-hashName (SkeinHash size) = "SKEIN" ++ show size--hashNameE :: Hash -> String-hashNameE hash = hashName hash ++ "E"+hashKeyVariety :: Hash -> HasExt -> KeyVariety+hashKeyVariety MD5Hash = MD5Key+hashKeyVariety SHA1Hash = SHA1Key+hashKeyVariety (SHA2Hash size) = SHA2Key size+hashKeyVariety (SHA3Hash size) = SHA3Key size+hashKeyVariety (SkeinHash size) = SKEINKey size  {- A key is a hash of its contents. -} keyValue :: Hash -> KeySource -> Annex (Maybe Key)@@ -82,7 +75,7 @@ 	s <- hashFile hash file filesize 	return $ Just $ stubKey 		{ keyName = s-		, keyBackendName = hashName hash+		, keyVariety = hashKeyVariety hash (HasExt False) 		, keySize = Just filesize 		} @@ -92,7 +85,7 @@   where 	addE k = return $ Just $ k 		{ keyName = keyName k ++ selectExtension (keyFilename source)-		, keyBackendName = hashNameE hash+		, keyVariety = hashKeyVariety hash (HasExt True) 		}  selectExtension :: FilePath -> String@@ -149,24 +142,29 @@ trivialMigrate :: Key -> Backend -> AssociatedFile -> Maybe Key trivialMigrate oldkey newbackend afile 	{- Fast migration from hashE to hash backend. -}-	| keyBackendName oldkey == name newbackend ++ "E" = Just $ oldkey+	| migratable && hasExt newvariety = Just $ oldkey 		{ keyName = keyHash oldkey-		, keyBackendName = name newbackend+		, keyVariety = newvariety 		} 	{- Fast migration from hash to hashE backend. -}-	| keyBackendName oldkey ++"E" == name newbackend = case afile of+	| migratable && hasExt oldvariety = case afile of 		Nothing -> Nothing 		Just file -> Just $ oldkey 			{ keyName = keyHash oldkey ++ selectExtension file-			, keyBackendName = name newbackend+			, keyVariety = newvariety 			} 	| otherwise = Nothing+  where+	migratable = oldvariety /= newvariety +		&& sameExceptExt oldvariety newvariety+	oldvariety = keyVariety oldkey+	newvariety = backendVariety newbackend  hashFile :: Hash -> FilePath -> Integer -> Annex String hashFile hash file filesize = go hash   where 	go MD5Hash = use md5Hasher-	go SHA1Hash = usehasher 1+	go SHA1Hash = usehasher (HashSize 1) 	go (SHA2Hash hashsize) = usehasher hashsize 	go (SHA3Hash hashsize) = use (sha3Hasher hashsize) 	go (SkeinHash hashsize) = use (skeinHasher hashsize)@@ -176,10 +174,10 @@ 		-- Force full evaluation so file is read and closed. 		return (length h `seq` h) 	-	usehasher hashsize = case shaHasher hashsize filesize of+	usehasher hashsize@(HashSize sz) = case shaHasher hashsize filesize of 		Left sha -> use sha 		Right (external, internal) -> do-			v <- liftIO $ externalSHA external hashsize file+			v <- liftIO $ externalSHA external sz file 			case v of 				Right r -> return r 				Left e -> do@@ -189,7 +187,7 @@ 					use internal  shaHasher :: HashSize -> Integer -> Either (L.ByteString -> String) (String, L.ByteString -> String)-shaHasher hashsize filesize+shaHasher (HashSize hashsize) filesize 	| hashsize == 1 = use SysConfig.sha1 sha1 	| hashsize == 256 = use SysConfig.sha256 sha2_256 	| hashsize == 224 = use SysConfig.sha224 sha2_224@@ -209,17 +207,15 @@ 	usehasher hasher = show . hasher  sha3Hasher :: HashSize -> (L.ByteString -> String)-sha3Hasher hashsize-#ifdef WITH_CRYPTONITE+sha3Hasher (HashSize hashsize) 	| hashsize == 256 = show . sha3_256 	| hashsize == 224 = show . sha3_224 	| hashsize == 384 = show . sha3_384 	| hashsize == 512 = show . sha3_512-#endif 	| otherwise = error $ "unsupported SHA3 size " ++ show hashsize  skeinHasher :: HashSize -> (L.ByteString -> String)-skeinHasher hashsize +skeinHasher (HashSize hashsize) 	| hashsize == 256 = show . skein256 	| hashsize == 512 = show . skein512 	| otherwise = error $ "unsupported SKEIN size " ++ show hashsize@@ -236,7 +232,7 @@  -} testKeyBackend :: Backend testKeyBackend = -	let b = genBackendE (SHA2Hash 256)+	let b = genBackendE (SHA2Hash (HashSize 256)) 	in b { getKey = (fmap addE) <$$> getKey b }    where 	addE k = k { keyName = keyName k ++ longext }
Backend/URL.hs view
@@ -11,6 +11,7 @@ ) where  import Annex.Common+import Types.Key import Types.Backend import Backend.Utilities @@ -19,7 +20,7 @@  backend :: Backend backend = Backend-	{ name = "URL"+	{ backendVariety = URLKey 	, getKey = const $ return Nothing 	, verifyKeyContent = Nothing 	, canUpgradeKey = Nothing@@ -33,6 +34,6 @@ fromUrl :: String -> Maybe Integer -> Key fromUrl url size = stubKey 	{ keyName = genKeyName url-	, keyBackendName = "URL"+	, keyVariety = URLKey 	, keySize = size 	}
Backend/WORM.hs view
@@ -8,6 +8,7 @@ module Backend.WORM (backends) where  import Annex.Common+import Types.Key import Types.Backend import Types.KeySource import Backend.Utilities@@ -18,7 +19,7 @@  backend :: Backend backend = Backend-	{ name = "WORM"+	{ backendVariety = WORMKey 	, getKey = keyValue 	, verifyKeyContent = Nothing 	, canUpgradeKey = Nothing@@ -37,7 +38,7 @@ 	relf <- getTopFilePath <$> inRepo (toTopFilePath $ keyFilename source) 	return $ Just $ stubKey 		{ keyName = genKeyName relf-		, keyBackendName = name backend+		, keyVariety = WORMKey 		, keySize = Just sz 		, keyMtime = Just $ modificationTime stat 		}
Build/Configure.hs view
@@ -34,7 +34,7 @@ 	, TestCase "rsync" $ requireCmd "rsync" "rsync --version >/dev/null" 	, TestCase "curl" $ testCmd "curl" "curl --version >/dev/null" 	, TestCase "wget" $ testCmd "wget" "wget --version >/dev/null"-	, TestCase "wget supports -q --show-progress" checkWgetQuietProgress+	, TestCase "wget unclutter options" checkWgetUnclutter 	, TestCase "bup" $ testCmd "bup" "bup --version >/dev/null" 	, TestCase "nice" $ testCmd "nice" "nice true >/dev/null" 	, TestCase "ionice" $ testCmd "ionice" "ionice -c3 true >/dev/null"@@ -105,8 +105,8 @@ 			error $ "installed git version " ++ show v ++ " is too old! (Need " ++ show oldestallowed ++ " or newer)" 		return $ Config "gitversion" $ StringConfig $ show v -checkWgetQuietProgress :: Test-checkWgetQuietProgress = Config "wgetquietprogress" . BoolConfig+checkWgetUnclutter :: Test+checkWgetUnclutter = Config "wgetunclutter" . BoolConfig 	. maybe False (>= normalize "1.16") 	<$> getWgetVersion  
Build/DistributionUpdate.hs view
@@ -119,13 +119,14 @@ 			Just k -> whenM (inAnnex k) $ do 				liftIO $ putStrLn f 				let infofile = f ++ ".info"-				liftIO $ writeFile infofile $ show $ GitAnnexDistribution+				let d = GitAnnexDistribution 					{ distributionUrl = mkUrl f 					, distributionKey = k 					, distributionVersion = bv 					, distributionReleasedate = now 					, distributionUrgentUpgrade = Nothing 					}+				liftIO $ writeFile infofile $ formatInfoFile d 				void $ inRepo $ runBool [Param "add", File infofile] 				signFile infofile 				signFile f
Build/EvilLinker.hs view
@@ -127,7 +127,7 @@ 	putStrLn $ unwords [c, show ps] 	systemenviron <- getEnvironment 	let environ' = fromMaybe [] environ ++ systemenviron-	out@(_, ok) <- processTranscript' (\p -> p { Utility.Process.env = Just environ' }) c ps Nothing+	out@(_, ok) <- processTranscript' ((proc c ps) { Utility.Process.env = Just environ' }) Nothing 	putStrLn $ unwords [c, "finished", show ok] 	return out 
Build/OSXMkLibs.hs view
@@ -13,7 +13,6 @@ import Control.Monad import Control.Monad.IfElse import Data.List-import Data.String.Utils import Control.Applicative import Prelude 
CHANGELOG view
@@ -1,3 +1,71 @@+git-annex (6.20170301) unstable; urgency=medium++  * No changes from 6.20170228; a new version number was needed due+    to a problem with Hackage.++ -- Joey Hess <id@joeyh.name>  Wed, 01 Mar 2017 12:06:02 -0400++git-annex (6.20170228) unstable; urgency=medium++  * Cryptographically secure hashes can be forced to be used in a+    repository, by setting annex.securehashesonly.+    This does not prevent the git repository from containing links+    to insecure hashes, but it does prevent the content of such files+    from being added to .git/annex/objects by any method.+  * Tighten key parser to prevent SHA1 collision attacks generating+    two keys that have the same SHA1. (Only done for keys that contain+    a hash). This ensures that signed git commits of annexed files+    will remain secure, as long as git-annex is using a secure hashing+    backend.+  * fsck: Warn about any files whose content is present, that don't+    use secure hashes, when annex.securehashesonly is set.+  * init: When annex.securehashesonly has been set with git-annex config,+    copy that value to the annex.securehashesonly git config.+  * Added --securehash option to match files using a secure hash function,+    and corresponding securehash preferred content expression.+  * sync, merge: Fail when the current branch has no commits yet, instead+    of not merging in anything from remotes and appearing to succeed.+  * Run ssh with -n whenever input is not being piped into it,+    to avoid it consuming stdin that it shouldn't.+    This fixes git-annex-checkpresentkey --batch remote,+    which didn't output results for all keys passed into it. Other+    git-annex commands that communicate with a remote over ssh may also+    have been consuming stdin that they shouldn't have, which could have+    impacted using them in eg, shell scripts.+  * sync: Improve integration with receive.denyCurrentBranch=updateInstead,+    displaying error messages from the remote then it fails to update+    its checked out branch.+  * Added post-recieve hook, which makes updateInstead work with direct+    mode and adjusted branches.+  * init: Set up the post-receive hook.+  * sync: When syncing with a local repository located on a crippled+    filesystem, run the post-receive hook there, since it wouldn't get run+    otherwise. This makes pushing to repos on FAT-formatted removable+    drives update them when receive.denyCurrentBranch=updateInstead.+  * config group groupwanted numcopies schedule wanted required: +    Avoid displaying extraneous messages about repository auto-init,+    git-annex branch merging, etc, when being used to get information.+  * adjust: Fix behavior when used in a repository that contains+    submodules.+  * Run wget with -nv instead of -q, so it will display HTTP errors.+  * Run curl with -S, so HTTP errors are displayed, even when+    it's otherwise silent.+  * When downloading in --json or --quiet mode, use curl in preference+    to wget, since curl is able to display only errors to stderr, unlike+    wget.+  * status: Pass --ignore-submodules=when option on to git status.+  * config --set: As well as setting value in git-annex branch,+    set local gitconfig. This is needed especially for+    annex.securehashesonly, which is read only from local gitconfig and not+    the git-annex branch.+  * Removed support for building with the old cryptohash library.+    Building with that library made git-annex not support SHA3; it's time+    for that to always be supported in case SHA2 dominoes.+  * git-annex.cabal: Make crypto-api a dependency even when built w/o+    webapp and test suite.++ -- Joey Hess <id@joeyh.name>  Tue, 28 Feb 2017 14:39:47 -0400+ git-annex (6.20170214) unstable; urgency=medium    * Increase default cost for p2p remotes from 200 to 1000.
CmdLine/GitAnnex.hs view
@@ -61,6 +61,7 @@ import qualified Command.Unlock import qualified Command.Lock import qualified Command.PreCommit+import qualified Command.PostReceive import qualified Command.Find import qualified Command.FindRef import qualified Command.Whereis@@ -148,6 +149,7 @@ 	, Command.Uninit.cmd 	, Command.Reinit.cmd 	, Command.PreCommit.cmd+	, Command.PostReceive.cmd 	, Command.NumCopies.cmd 	, Command.Trust.cmd 	, Command.Untrust.cmd
CmdLine/GitAnnex/Options.hs view
@@ -15,6 +15,7 @@ import qualified Git.Config import qualified Git.Construct import Git.Types+import Types.Key import Types.TrustLevel import Types.NumCopies import Types.Messages@@ -223,6 +224,11 @@ 		<> hidden 		<> completeBackends 		)+	, globalFlag Limit.addSecureHash+		( long "securehash"+		<> help "match files using a cryptographically secure hash"+		<> hidden+		) 	, globalSetter Limit.addInAllGroup $ strOption 		( long "inallgroup" <> metavar paramGroup 		<> help "match files present in all remotes in a group"@@ -346,4 +352,5 @@ 		 		 completeBackends :: HasCompleter f => Mod f a-completeBackends = completeWith (map Backend.name Backend.list)+completeBackends = completeWith $+	map (formatKeyVariety . Backend.backendVariety) Backend.list
CmdLine/GlobalSetter.hs view
@@ -20,5 +20,5 @@ globalSetter setter parser = DeferredParse . setter <$> parser  combineGlobalOptions :: [GlobalOption] -> Parser GlobalSetter-combineGlobalOptions l = DeferredParse . sequence_ . map getParsed+combineGlobalOptions l = DeferredParse . mapM_ getParsed 	<$> many (foldl1 (<|>) l)
Command.hs view
@@ -22,12 +22,14 @@ import CmdLine.GitAnnex.Options as ReExported import CmdLine.Batch as ReExported import Options.Applicative as ReExported hiding (command)+import qualified Annex import qualified Git import Annex.Init import Config import Utility.Daemon import Types.Transfer import Types.ActionItem+import Types.Messages  {- Generates a normal Command -} command :: String -> CommandSection -> String -> CmdParamsDesc -> (CmdParamsDesc -> CommandParser) -> Command@@ -62,6 +64,16 @@  - etc. -} noMessages :: Command -> Command noMessages c = c { cmdnomessages = True }++{- Undoes noMessages -}+allowMessages :: Annex ()+allowMessages = do+	curr <- Annex.getState Annex.output+	case outputType curr of+		QuietOutput -> Annex.setOutput NormalOutput+		_ -> noop+	Annex.changeState $ \s -> s+		{ Annex.output = (Annex.output s) { implicitMessages = True } }  {- Adds a fallback action to a command, that will be run if it's used  - outside a git repository. -}
Command/AddUrl.hs view
@@ -356,10 +356,13 @@   where 	go = do 		maybeShowJSON $ JSONChunk [("key", key2file key)]-		when (isJust mtmp) $-			logStatus key InfoPresent 		setUrlPresent u key url-		addAnnexedFile file key mtmp+		ifM (addAnnexedFile file key mtmp)+			( do+				when (isJust mtmp) $+					logStatus key InfoPresent+			, liftIO $ maybe noop nukeFile mtmp+			)  nodownload :: URLString -> Url.UrlInfo -> FilePath -> Annex (Maybe Key) nodownload url urlinfo file
Command/Config.hs view
@@ -9,9 +9,11 @@  import Command import Logs.Config+import Config  cmd :: Command-cmd = command "config" SectionSetup "configuration stored in git-annex branch"+cmd = noMessages $ command "config" SectionSetup+	"configuration stored in git-annex branch" 	paramNothing (seek <$$> optParser)  data Action@@ -47,14 +49,18 @@  seek :: Action -> CommandSeek seek (SetConfig name val) = commandAction $ do+	allowMessages 	showStart name val 	next $ next $ do 		setGlobalConfig name val+		setConfig (ConfigKey name) val 		return True seek (UnsetConfig name) = commandAction $ do+	allowMessages 	showStart name "unset" 	next $ next $ do 		unsetGlobalConfig name+		unsetConfig (ConfigKey name) 		return True seek (GetConfig name) = commandAction $ do 	mv <- getGlobalConfig name
Command/Find.hs view
@@ -13,6 +13,7 @@ import Command import Annex.Content import Limit+import Types.Key import qualified Utility.Format import Utility.DataUnits @@ -76,7 +77,7 @@ keyVars :: Key -> [(String, String)] keyVars key = 	[ ("key", key2file key)-	, ("backend", keyBackendName key)+	, ("backend", formatKeyVariety $ keyVariety key) 	, ("bytesize", size show) 	, ("humansize", size $ roughSize storageUnits True) 	, ("keyname", keyName key)
Command/Fsck.hs view
@@ -1,6 +1,6 @@ {- git-annex command  -- - Copyright 2010-2016 Joey Hess <id@joeyh.name>+ - Copyright 2010-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -35,6 +35,7 @@ import qualified Database.Keys import qualified Database.Fsck as FsckDb import Types.CleanupActions+import Types.Key  import Data.Time.Clock.POSIX import System.Posix.Types (EpochTime)@@ -175,7 +176,7 @@  startKey :: Maybe Remote -> Incremental -> Key -> ActionItem -> NumCopies -> CommandStart startKey from inc key ai numcopies =-	case Backend.maybeLookupBackendName (keyBackendName key) of+	case Backend.maybeLookupBackendVariety (keyVariety key) of 		Nothing -> stop 		Just backend -> runFsck inc ai key $ 			case from of@@ -233,6 +234,14 @@ 			warning $ "** Unable to set correct write mode for " ++ obj ++ " ; perhaps you don't own that file" 	whenM (liftIO $ doesDirectoryExist $ parentDir obj) $ 		freezeContentDir obj++	{- Warn when annex.securehashesonly is set and content using an +	 - insecure hash is present. This should only be able to happen+	 - if the repository already contained the content before the+	 - config was set. -}+	when (present && not (cryptographicallySecure (keyVariety key))) $+		whenM (annexSecureHashesOnly <$> Annex.getGitConfig) $+			warning $ "** Despite annex.securehashesonly being set, " ++ obj ++ " has content present in the annex using an insecure " ++ formatKeyVariety (keyVariety key) ++ " key"  	{- In direct mode, modified files will show up as not present, 	 - but that is expected and not something to do anything about. -}
Command/Group.hs view
@@ -15,7 +15,7 @@ import qualified Data.Set as S  cmd :: Command-cmd = command "group" SectionSetup "add a repository to a group"+cmd = noMessages $ command "group" SectionSetup "add a repository to a group" 	(paramPair paramRemote paramDesc) (withParams seek)  seek :: CmdParams -> CommandSeek@@ -23,12 +23,13 @@  start :: [String] -> CommandStart start (name:g:[]) = do+	allowMessages 	showStart "group" name 	u <- Remote.nameToUUID name 	next $ setGroup u g start (name:[]) = do 	u <- Remote.nameToUUID name-	showRaw . unwords . S.toList =<< lookupGroups u+	liftIO . putStrLn . unwords . S.toList =<< lookupGroups u 	stop start _ = giveup "Specify a repository and a group." 
Command/GroupWanted.hs view
@@ -12,7 +12,7 @@ import Command.Wanted (performGet, performSet)  cmd :: Command-cmd = command "groupwanted" SectionSetup +cmd = noMessages $ command "groupwanted" SectionSetup  	"get or set groupwanted expression" 	(paramPair paramGroup (paramOptional paramExpression)) 	(withParams seek)@@ -23,6 +23,7 @@ start :: [String] -> CommandStart start (g:[]) = next $ performGet groupPreferredContentMapRaw g start (g:expr:[]) = do+	allowMessages 	showStart "groupwanted" g 	next $ performSet groupPreferredContentSet expr g start _ = giveup "Specify a group."
Command/ImportFeed.hs view
@@ -112,8 +112,9 @@ getCache opttemplate = ifM (Annex.getState Annex.force) 	( ret S.empty S.empty 	, do-		showAction "checking known urls"+		showStart "importfeed" "checking known urls" 		(is, us) <- unzip <$> (mapM knownItems =<< knownUrls)+		showEndOk 		ret (S.fromList us) (S.fromList (concat is)) 	)   where@@ -138,12 +139,10 @@ 			Just $ ToDownload f u i $ Enclosure enclosureurl 		Nothing -> mkquvi f i 	mkquvi f i = case getItemLink i of-		Just link -> do-			liftIO $ print ("link", link)-			ifM (quviSupported link)-				( return $ Just $ ToDownload f u i $ QuviLink link-				, return Nothing-				)+		Just link -> ifM (quviSupported link)+			( return $ Just $ ToDownload f u i $ QuviLink link+			, return Nothing+			) 		Nothing -> return Nothing  {- Feeds change, so a feed download cannot be resumed. -}
Command/Indirect.hs view
@@ -86,16 +86,16 @@ 		whenM (liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f) $ do 			v <- tryNonAsync (moveAnnex k f) 			case v of-				Right _ -> do +				Right True -> do  					l <- calcRepo $ gitAnnexLink f k 					liftIO $ createSymbolicLink l f-				Left e -> catchNonAsync (restoreFile f k e)-					warnlocked+				Right False -> warnlocked "Failed to move file to annex"+				Left e -> catchNonAsync (restoreFile f k e) $+					warnlocked . show 		showEndOk -	warnlocked :: SomeException -> Annex ()-	warnlocked e = do-		warning $ show e+	warnlocked msg = do+		warning msg 		warning "leaving this file as-is; correct this problem and run git annex add on it" 	 cleanup :: CommandCleanup
Command/Info.hs view
@@ -36,6 +36,7 @@ import Utility.Percentage import Types.Transfer import Logs.Transfer+import Types.Key import Types.TrustLevel import Types.FileMatcher import qualified Limit@@ -51,7 +52,7 @@ 	{ countKeys :: Integer 	, sizeKeys :: Integer 	, unknownSizeKeys :: Integer-	, backendsKeys :: M.Map String Integer+	, backendsKeys :: M.Map KeyVariety Integer 	}  data NumCopiesStats = NumCopiesStats@@ -418,14 +419,14 @@   where 	desc = "transfers in progress" 	line uuidmap t i = unwords-		[ showLcDirection (transferDirection t) ++ "ing"+		[ formatDirection (transferDirection t) ++ "ing" 		, fromMaybe (key2file $ transferKey t) (associatedFile i) 		, if transferDirection t == Upload then "to" else "from" 		, maybe (fromUUID $ transferUUID t) Remote.name $ 			M.lookup (transferUUID t) uuidmap 		] 	jsonify t i = object $ map (\(k, v) -> (T.pack k, v)) $-		[ ("transfer", toJSON (showLcDirection (transferDirection t)))+		[ ("transfer", toJSON (formatDirection (transferDirection t))) 		, ("key", toJSON (key2file (transferKey t))) 		, ("file", toJSON (associatedFile i)) 		, ("remote", toJSON (fromUUID (transferUUID t)))@@ -451,7 +452,8 @@  backend_usage :: Stat backend_usage = stat "backend usage" $ json fmt $-	ObjectMap . backendsKeys <$> cachedReferencedData+	ObjectMap . (M.mapKeys formatKeyVariety) . backendsKeys+		<$> cachedReferencedData   where 	fmt = multiLine . map (\(b, n) -> b ++ ": " ++ show n) . sort . M.toList . fromObjectMap @@ -598,7 +600,7 @@ 	{- All calculations strict to avoid thunks when repeatedly 	 - applied to many keys. -} 	!count' = count + 1-	!backends' = M.insertWith (+) (keyBackendName key) 1 backends+	!backends' = M.insertWith (+) (keyVariety key) 1 backends 	!size' = maybe size (+ size) ks 	!unknownsize' = maybe (unknownsize + 1) (const unknownsize) ks 	ks = keySize key
Command/Map.hs view
@@ -16,6 +16,7 @@ import qualified Git.Construct import qualified Remote import qualified Annex+import Annex.Ssh import Annex.UUID import Logs.UUID import Logs.Trust@@ -219,10 +220,11 @@ 	  where 		p = proc pcmd $ toCommand params -	configlist = Ssh.onRemote r (pipedconfig, return Nothing) "configlist" [] []+	configlist = Ssh.onRemote NoConsumeStdin r+		(pipedconfig, return Nothing) "configlist" [] [] 	manualconfiglist = do 		gc <- Annex.getRemoteGitConfig r-		sshparams <- Ssh.toRepo r gc [Param sshcmd]+		sshparams <- Ssh.toRepo NoConsumeStdin r gc [Param sshcmd] 		liftIO $ pipedconfig "ssh" sshparams 	  where 		sshcmd = "sh -c " ++ shellEscape
Command/Merge.hs view
@@ -17,9 +17,9 @@ 	paramNothing (withParams seek)  seek :: CmdParams -> CommandSeek-seek ps = do-	withNothing mergeBranch ps-	withNothing mergeSynced ps+seek _ = do+	commandAction mergeBranch+	commandAction mergeSynced  mergeBranch :: CommandStart mergeBranch = do
Command/NumCopies.hs view
@@ -10,10 +10,9 @@ import Command import qualified Annex import Annex.NumCopies-import Types.Messages  cmd :: Command-cmd = command "numcopies" SectionSetup +cmd = noMessages $ command "numcopies" SectionSetup  	"configure desired number of copies" 	paramNumber (withParams seek) @@ -35,7 +34,6 @@  startGet :: CommandStart startGet = next $ next $ do-	Annex.setOutput QuietOutput 	v <- getGlobalNumCopies 	case v of 		Just n -> liftIO $ putStrLn $ show $ fromNumCopies n@@ -49,6 +47,7 @@  startSet :: Int -> CommandStart startSet n = do+	allowMessages 	showStart "numcopies" (show n) 	next $ next $ do 		setGlobalNumCopies $ NumCopies n
+ Command/PostReceive.hs view
@@ -0,0 +1,51 @@+{- git-annex command+ -+ - Copyright 2017 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Command.PostReceive where++import Command+import qualified Annex+import Git.Types+import Annex.UpdateInstead+import Command.Sync (mergeLocal, prepMerge, mergeConfig, getCurrBranch)++-- This does not need to modify the git-annex branch to update the +-- work tree, but auto-initialization might change the git-annex branch.+-- Since it would be surprising for a post-receive hook to make such a+-- change, that's prevented by noCommit.+cmd :: Command+cmd = noCommit $+	command "post-receive" SectionPlumbing+		"run by git post-receive hook"+		paramNothing+		(withParams seek)++seek :: CmdParams -> CommandSeek+seek _ = whenM needUpdateInsteadEmulation $ do+	fixPostReceiveHookEnv+	commandAction updateInsteadEmulation++{- When run by the post-receive hook, the cwd is the .git directory, + - and GIT_DIR=. It's not clear why git does this.+ -+ - Fix up from that unusual situation, so that git commands+ - won't try to treat .git as the work tree. -}+fixPostReceiveHookEnv :: Annex ()+fixPostReceiveHookEnv = do+	g <- Annex.gitRepo+	case location g of+		Local { gitdir = ".", worktree = Just "." } ->+			Annex.adjustGitRepo $ \g' -> pure $ g'+				{ location = (location g')+					{ worktree = Just ".." }+				}+		_ -> noop++updateInsteadEmulation :: CommandStart+updateInsteadEmulation = do+	prepMerge+	mergeLocal mergeConfig =<< join getCurrBranch
Command/Reinject.hs view
@@ -74,9 +74,8 @@ 	, error "failed" 	)   where-	move = checkDiskSpaceToGet key False $ do+	move = checkDiskSpaceToGet key False $ 		moveAnnex key src-		return True  cleanup :: Key -> CommandCleanup cleanup key = do
Command/Schedule.hs view
@@ -8,16 +8,14 @@ module Command.Schedule where  import Command-import qualified Annex import qualified Remote import Logs.Schedule import Types.ScheduledActivity-import Types.Messages  import qualified Data.Set as S  cmd :: Command-cmd = command "schedule" SectionSetup "get or set scheduled jobs"+cmd = noMessages $ command "schedule" SectionSetup "get or set scheduled jobs" 	(paramPair paramRemote (paramOptional paramExpression)) 	(withParams seek) @@ -29,6 +27,7 @@   where 	parse (name:[]) = go name performGet 	parse (name:expr:[]) = go name $ \uuid -> do+		allowMessages 		showStart "schedule" name 		performSet expr uuid 	parse _ = giveup "Specify a repository."@@ -39,7 +38,6 @@  performGet :: UUID -> CommandPerform performGet uuid = do-	Annex.setOutput QuietOutput 	s <- scheduleGet uuid 	liftIO $ putStrLn $ intercalate "; " $  		map fromScheduledActivity $ S.toList s
Command/Smudge.hs view
@@ -84,7 +84,7 @@ 				-- for this file before, so that when 				-- git re-cleans a file its backend does 				-- not change.-				currbackend <- maybe Nothing (maybeLookupBackendName . keyBackendName)+				currbackend <- maybe Nothing (maybeLookupBackendVariety . keyVariety) 					<$> catKeyFile file 				liftIO . emitPointer 					=<< go
Command/Status.hs view
@@ -20,14 +20,28 @@ 	withGlobalOptions [jsonOption] $ 		command "status" SectionCommon 			"show the working tree status"-			paramPaths (withParams seek)+			paramPaths (seek <$$> optParser) -seek :: CmdParams -> CommandSeek-seek = withWords start+data StatusOptions = StatusOptions+	{ statusFiles :: CmdParams+	, ignoreSubmodules :: Maybe String+	}++optParser :: CmdParamsDesc -> Parser StatusOptions+optParser desc = StatusOptions+	<$> cmdParams desc+	<*> optional (strOption+		( long "ignore-submodules"+		<> help "passed on to git status"+		<> metavar "WHEN"+		))++seek :: StatusOptions -> CommandSeek+seek o = withWords (start o) (statusFiles o) 	-start :: [FilePath] -> CommandStart-start locs = do-	(l, cleanup) <- inRepo $ getStatus locs+start :: StatusOptions -> [FilePath] -> CommandStart+start o locs = do+	(l, cleanup) <- inRepo $ getStatus ps locs 	getstatus <- ifM isDirect 		( return statusDirect 		, return $ \s -> pure (Just s)@@ -35,6 +49,10 @@ 	forM_ l $ \s -> maybe noop displayStatus =<< getstatus s 	void $ liftIO cleanup 	stop+  where+	ps = case ignoreSubmodules o of+		Nothing -> []+		Just s -> [Param $ "--ignore-submodules="++s]  displayStatus :: Status -> Annex () -- renames not shown in this simplified status
Command/Sync.hs view
@@ -1,7 +1,7 @@ {- git-annex command  -  - Copyright 2011 Joachim Breitner <mail@joachim-breitner.de>- - Copyright 2011-2016 Joey Hess <id@joeyh.name>+ - Copyright 2011-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -40,6 +40,7 @@ import qualified Remote.Git import Config import Config.GitConfig+import Config.Files import Annex.Wanted import Annex.Content import Command.Get (getKey')@@ -51,6 +52,7 @@ import Annex.AdjustedBranch import Annex.Ssh import Annex.BloomFilter+import Annex.UpdateInstead import Utility.Bloom import Utility.OptParse @@ -272,22 +274,37 @@ 	return True  mergeLocal :: [Git.Merge.MergeConfig] -> CurrBranch -> CommandStart-mergeLocal mergeconfig currbranch@(Just branch, madj) = go =<< needmerge+mergeLocal mergeconfig currbranch@(Just _, _) =+	go =<< needMerge currbranch   where-	syncbranch = syncBranch branch-	needmerge = ifM isBareRepo-		( return False-		, ifM (inRepo $ Git.Ref.exists syncbranch)-			( inRepo $ Git.Branch.changed branch' syncbranch-			, return False-			)-		)-	go False = stop-	go True = do+	go Nothing = stop+	go (Just syncbranch) = do 		showStart "merge" $ Git.Ref.describe syncbranch 		next $ next $ merge currbranch mergeconfig Git.Branch.ManualCommit syncbranch+mergeLocal _ (Nothing, madj) = do+	b <- inRepo Git.Branch.currentUnsafe+	ifM (isJust <$> needMerge (b, madj))+		( do+			warning $ "There are no commits yet in the currently checked out branch, so cannot merge any remote changes into it."+			next $ next $ return False+		, stop+		)++-- Returns the branch that should be merged, if any.+needMerge :: CurrBranch -> Annex (Maybe Git.Branch)+needMerge (Nothing, _) = return Nothing+needMerge (Just branch, madj) = ifM (allM id checks)+	( return (Just syncbranch)+	, return Nothing+	)+  where+	checks =+		[ not <$> isBareRepo+		, inRepo (Git.Ref.exists syncbranch)+		, inRepo (Git.Branch.changed branch' syncbranch)+		]+	syncbranch = syncBranch branch 	branch' = maybe branch (adjBranch . originalToAdjusted branch) madj-mergeLocal _ (Nothing, _) = stop  pushLocal :: CurrBranch -> CommandStart pushLocal b = do@@ -362,30 +379,48 @@ 		showOutput 		ok <- inRepoWithSshOptionsTo (Remote.repo remote) (Remote.gitconfig remote) $ 			pushBranch remote branch-		unless ok $ do-			warning $ unwords [ "Pushing to " ++ Remote.name remote ++ " failed." ]-			showLongNote "(non-fast-forward problems can be solved by setting receive.denyNonFastforwards to false in the remote's git config)"-		return ok+		if ok+			then postpushupdate+			else do+				warning $ unwords [ "Pushing to " ++ Remote.name remote ++ " failed." ]+				showLongNote "(non-fast-forward problems can be solved by setting receive.denyNonFastforwards to false in the remote's git config)"+				return ok   where 	needpush 		| remoteAnnexReadOnly (Remote.gitconfig remote) = return False 		| otherwise = anyM (newer remote) [syncBranch branch, Annex.Branch.name]+	-- Do updateInstead emulation for remotes on eg removable drives+	-- formatted FAT, where the post-update hook won't run.+	postpushupdate+		| maybe False annexCrippledFileSystem (remoteGitConfig (Remote.gitconfig remote)) = +			case Git.repoWorkTree (Remote.repo remote) of+				Nothing -> return True+				Just wt -> ifM (Remote.Git.onLocal remote needUpdateInsteadEmulation)+					( liftIO $ do+						p <- readProgramFile+						boolSystem' p [Param "post-receive"]+							(\cp -> cp { cwd = Just wt })+					, return True+					)+		| otherwise = return True  {- Pushes a regular branch like master to a remote. Also pushes the git-annex  - branch.  -- - If the remote is a bare git repository, it's best to push the regular + - If the remote is a bare git repository, it's best to push the regular  - branch directly to it, so that cloning/pulling will get it.  - On the other hand, if it's not bare, pushing to the checked out branch- - will fail, and this is why we push to its syncBranch.+ - will generally fail (except with receive.denyCurrentBranch=updateInstead),+ - and this is why we push to its syncBranch.  -  - Git offers no way to tell if a remote is bare or not, so both methods  - are tried.  -- - The direct push is likely to spew an ugly error message, so stderr is- - elided. Since git progress display goes to stderr too, the sync push- - is done first, and actually sends the data. Then the direct push is- - tried, with stderr discarded, to update the branch ref on the remote.+ - The direct push is likely to spew an ugly error message, so its stderr is+ - often elided. Since git progress display goes to stderr too, the + - sync push is done first, and actually sends the data. Then the+ - direct push is tried, with stderr discarded, to update the branch ref+ - on the remote.  -  - The sync push forces the update of the remote synced/git-annex branch.  - This is necessary if a transition has rewritten the git-annex branch.@@ -401,16 +436,30 @@  - set on the remote.  -} pushBranch :: Remote -> Git.Branch -> Git.Repo -> IO Bool-pushBranch remote branch g = tryIO (directpush g) `after` syncpush g+pushBranch remote branch g = directpush `after` annexpush `after` syncpush   where-	syncpush = Git.Command.runBool $ pushparams+	syncpush = flip Git.Command.runBool g $ pushparams 		[ Git.Branch.forcePush $ refspec Annex.Branch.name 		, refspec $ fromAdjustedBranch branch 		]-	directpush = Git.Command.runQuiet $ pushparams-		[ Git.fromRef $ Git.Ref.base $ Annex.Branch.name-		, Git.fromRef $ Git.Ref.base $ fromDirectBranch $ fromAdjustedBranch branch-		]+	annexpush = void $ tryIO $ flip Git.Command.runQuiet g $ pushparams+		[ Git.fromRef $ Git.Ref.base $ Annex.Branch.name ]+	directpush = do+		-- Git prints out an error message when this fails.+		-- In the default configuration of receive.denyCurrentBranch,+		-- the error message mentions that config setting+		-- (and should even if it is localized), and is quite long,+		-- and the user was not intending to update the checked out+		-- branch, so in that case, avoid displaying the error+		-- message. Do display other error messages though,+		-- including the error displayed when+		-- receive.denyCurrentBranch=updateInstead -- the user+		-- will want to see that one.+		let p = flip Git.Command.gitCreateProcess g $ pushparams+			[ Git.fromRef $ Git.Ref.base $ fromDirectBranch $ fromAdjustedBranch branch ]+		(transcript, ok) <- processTranscript' p Nothing+		when (not ok && not ("denyCurrentBranch" `isInfixOf` transcript)) $+			hPutStr stderr transcript 	pushparams branches = 		[ Param "push" 		, Param $ Remote.name remote
Command/TestRemote.hs view
@@ -149,7 +149,7 @@ 		Annex.eval st (Annex.setOutput QuietOutput >> a) @? "failed" 	present b = check ("present " ++ show b) $ 		(== Right b) <$> Remote.hasKey r k-	fsck = case maybeLookupBackendName (keyBackendName k) of+	fsck = case maybeLookupBackendVariety (keyVariety k) of 		Nothing -> return True 		Just b -> case Backend.verifyKeyContent b of 			Nothing -> return True@@ -225,5 +225,5 @@ 		} 	k <- fromMaybe (error "failed to generate random key") 		<$> Backend.getKey Backend.Hash.testKeyBackend ks-	moveAnnex k f+	_ <- moveAnnex k f 	return k
Command/Version.hs view
@@ -11,6 +11,7 @@ import qualified Build.SysConfig as SysConfig import Annex.Version import BuildFlags+import Types.Key import qualified Types.Backend as B import qualified Types.Remote as R import qualified Remote@@ -62,7 +63,8 @@ showPackageVersion = do 	vinfo "git-annex version" SysConfig.packageversion 	vinfo "build flags" $ unwords buildFlags-	vinfo "key/value backends" $ unwords $ map B.name Backend.list+	vinfo "key/value backends" $ unwords $+		map (formatKeyVariety . B.backendVariety) Backend.list 	vinfo "remote types" $ unwords $ map R.typename Remote.remoteTypes  showRawVersion :: IO ()
Command/Wanted.hs view
@@ -8,10 +8,8 @@ module Command.Wanted where  import Command-import qualified Annex import qualified Remote import Logs.PreferredContent-import Types.Messages import Types.StandardGroups  import qualified Data.Map as M@@ -27,7 +25,8 @@ 	-> Annex (M.Map UUID PreferredContentExpression) 	-> (UUID -> PreferredContentExpression -> Annex ()) 	-> Command-cmd' name desc getter setter = command name SectionSetup desc pdesc (withParams seek)+cmd' name desc getter setter = noMessages $ +	command name SectionSetup desc pdesc (withParams seek)   where 	pdesc = paramPair paramRemote (paramOptional paramExpression) @@ -35,6 +34,7 @@  	start (rname:[]) = go rname (performGet getter) 	start (rname:expr:[]) = go rname $ \uuid -> do+		allowMessages 		showStart name rname 		performSet setter expr uuid 	start _ = giveup "Specify a repository."@@ -45,7 +45,6 @@  performGet :: Ord a => Annex (M.Map a PreferredContentExpression) -> a -> CommandPerform performGet getter a = do-	Annex.setOutput QuietOutput 	m <- getter 	liftIO $ putStrLn $ fromMaybe "" $ M.lookup a m 	next $ return True
Crypto.hs view
@@ -42,6 +42,7 @@ import qualified Utility.Gpg as Gpg import Types.Crypto import Types.Remote+import Types.Key  {- The beginning of a Cipher is used for MAC'ing; the remainder is used  - as the GPG symmetric encryption passphrase when using the hybrid@@ -159,14 +160,16 @@ encryptKey :: Mac -> Cipher -> EncKey encryptKey mac c k = stubKey 	{ keyName = macWithCipher mac c (key2file k)-	, keyBackendName = encryptedBackendNamePrefix ++ showMac mac+	, keyVariety = OtherKey (encryptedBackendNamePrefix ++ showMac mac) 	}  encryptedBackendNamePrefix :: String encryptedBackendNamePrefix = "GPG"  isEncKey :: Key -> Bool-isEncKey k = encryptedBackendNamePrefix `isPrefixOf` keyBackendName k+isEncKey k = case keyVariety k of+	OtherKey s ->  encryptedBackendNamePrefix `isPrefixOf` s+	_ -> False  type Feeder = Handle -> IO () type Reader m a = Handle -> m a
Database/Types.hs view
@@ -14,7 +14,7 @@ import Data.Char  import Utility.PartialPrelude-import Types.Key+import Key import Utility.InodeCache  -- A serialized Key
+ Git/ConfigTypes.hs view
@@ -0,0 +1,40 @@+{- git config types+ -+ - Copyright 2012, 2017 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Git.ConfigTypes where++import Data.Char++import Common+import Git+import qualified Git.Config++data SharedRepository = UnShared | GroupShared | AllShared | UmaskShared Int+	deriving (Eq)++getSharedRepository :: Repo -> SharedRepository+getSharedRepository r =+	case map toLower $ Git.Config.get "core.sharedrepository" "" r of+		"1" -> GroupShared+		"2" -> AllShared+		"group" -> GroupShared+		"true" -> GroupShared+		"all" -> AllShared+		"world" -> AllShared+		"everybody" -> AllShared+		v -> maybe UnShared UmaskShared (readish v)++data DenyCurrentBranch = UpdateInstead | RefusePush | WarnPush | IgnorePush+	deriving (Eq)++getDenyCurrentBranch :: Repo -> DenyCurrentBranch+getDenyCurrentBranch r =+	case map toLower $ Git.Config.get "receive.denycurrentbranch" "" r of+		"updateinstead" -> UpdateInstead+		"warn" -> WarnPush+		"ignore" -> IgnorePush+		_ -> RefusePush
Git/LsTree.hs view
@@ -24,6 +24,7 @@ import qualified Git.Filename  import Numeric+import Data.Char import System.Posix.Types  data TreeItem = TreeItem@@ -66,7 +67,9 @@ 		, File $ fromRef t 		] ++ map File fs -{- Parses a line of ls-tree output.+{- Parses a line of ls-tree output, in format:+ - mode SP type SP sha TAB file+ -  - (The --long format is not currently supported.) -} parseLsTree :: String -> TreeItem parseLsTree l = TreeItem @@ -76,12 +79,9 @@ 	, file = sfile 	}   where-	-- l = <mode> SP <type> SP <sha> TAB <file>-	-- All fields are fixed, so we can pull them out of-	-- specific positions in the line.-	(m, past_m) = splitAt 7 l-	(!t, past_t) = splitAt 4 past_m-	(!s, past_s) = splitAt shaSize $ Prelude.tail past_t-	!f = Prelude.tail past_s+	(m, past_m) = splitAt 7 l -- mode is 6 bytes+	(!t, past_t) = separate isSpace past_m+	(!s, past_s) = splitAt shaSize past_t+	!f = drop 1 past_s 	!smode = fst $ Prelude.head $ readOct m 	!sfile = asTopFilePath $ Git.Filename.decode f
− Git/SharedRepository.hs
@@ -1,28 +0,0 @@-{- git core.sharedRepository handling- -- - Copyright 2012 Joey Hess <id@joeyh.name>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Git.SharedRepository where--import Data.Char--import Common-import Git-import qualified Git.Config--data SharedRepository = UnShared | GroupShared | AllShared | UmaskShared Int--getSharedRepository :: Repo -> SharedRepository-getSharedRepository r =-	case map toLower $ Git.Config.get "core.sharedrepository" "" r of-		"1" -> GroupShared-		"2" -> AllShared-		"group" -> GroupShared-		"true" -> GroupShared-		"all" -> AllShared-		"world" -> AllShared-		"everybody" -> AllShared-		v -> maybe UnShared UmaskShared (readish v)
Git/Status.hs view
@@ -64,13 +64,14 @@ 	cparse '?' = Just Untracked 	cparse _ = Nothing -getStatus :: [FilePath] -> Repo -> IO ([Status], IO Bool)-getStatus l r = do-	(ls, cleanup) <- pipeNullSplit params r+getStatus :: [CommandParam] -> [FilePath] -> Repo -> IO ([Status], IO Bool)+getStatus ps fs r = do+	(ls, cleanup) <- pipeNullSplit ps' r 	return (parseStatusZ ls, cleanup)   where-	params =-		[ Param "status"-		, Param "-uall"-		, Param "-z"-		] ++ map File l+	ps' = concat+		[ [Param "status"]+		, ps+		, [ Param "-uall" , Param "-z"]+		, map File fs+		]
Git/Tree.hs view
@@ -35,11 +35,14 @@ 	deriving (Show)  data TreeContent+	-- A blob object in the tree. 	= TreeBlob TopFilePath FileMode Sha 	-- A subtree that is already recorded in git, with a known sha. 	| RecordedSubTree TopFilePath Sha [TreeContent] 	-- A subtree that has not yet been recorded in git. 	| NewSubTree TopFilePath [TreeContent]+	-- A commit object that is part of a tree (used for submodules)+	| TreeCommit TopFilePath FileMode Sha 	deriving (Show, Eq, Ord)  {- Gets the Tree for a Ref. -}@@ -93,6 +96,7 @@ 			TreeBlob f fm s -> mkTreeOutput fm BlobObject s f 			RecordedSubTree f s _ -> mkTreeOutput 0o040000 TreeObject s f 			NewSubTree _ _ -> error "recordSubTree internal error; unexpected NewSubTree"+			TreeCommit f fm s -> mkTreeOutput fm CommitObject s f 		hPutStr h "\NUL" -- signal end of tree to --batch 	receive h = getSha "mktree" (hGetLine h) @@ -152,6 +156,7 @@ 	go _ b@(TreeBlob _ _ _) = [b] 	go n' (RecordedSubTree _ _ l') = concatMap (go (n'-1)) l' 	go n' (NewSubTree _ l') = concatMap (go (n'-1)) l'+	go _ c@(TreeCommit _ _ _) = [c]  {- Applies an adjustment to items in a tree.  -@@ -200,6 +205,9 @@ 					else return $ RecordedSubTree (LsTree.file i) (LsTree.sha i) []  				let !modified' = modified || slmodified || wasmodified 				go h modified' (subtree : c) depth intree is'+			Just CommitObject -> do+				let ti = TreeCommit (LsTree.file i) (LsTree.mode i)  (LsTree.sha i)+				go h wasmodified (ti:c) depth intree is 			_ -> error ("unexpected object type \"" ++ LsTree.typeobj i ++ "\"") 		| otherwise = return (c, wasmodified, i:is) @@ -236,6 +244,9 @@ 					let st = RecordedSubTree (LsTree.file i) (LsTree.sha i) subtree 					in go (st:t) intree is' 				Left e -> Left e+			Just CommitObject ->+				let c = TreeCommit (LsTree.file i) (LsTree.mode i) (LsTree.sha i)+				in go (c:t) intree is 			_ -> parseerr ("unexpected object type \"" ++ LsTree.typeobj i ++ "\"") 		| otherwise = Right (t, i:is) 	parseerr = Left@@ -259,6 +270,7 @@ 	gitPath (TreeBlob f _ _) = gitPath f 	gitPath (RecordedSubTree f _ _) = gitPath f 	gitPath (NewSubTree f _) = gitPath f+	gitPath (TreeCommit f _ _) = gitPath f  inTopTree :: GitPath t => t -> Bool inTopTree = inTree "."
+ Key.hs view
@@ -0,0 +1,181 @@+{- git-annex Keys+ -+ - Copyright 2011-2017 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++{-# OPTIONS_GHC -fno-warn-orphans #-}++module Key (+	Key(..),+	AssociatedFile,+	stubKey,+	key2file,+	file2key,+	nonChunkKey,+	chunkKeyOffset,+	isChunkKey,+	isKeyPrefix,++	prop_isomorphic_key_encode,+	prop_isomorphic_key_decode+) where++import Data.Aeson+import Data.Char+import qualified Data.Text as T++import Common+import Types.Key+import Utility.QuickCheck+import Utility.Bloom+import qualified Utility.SimpleProtocol as Proto++stubKey :: Key+stubKey = Key+	{ keyName = ""+	, keyVariety = OtherKey ""+	, keySize = Nothing+	, keyMtime = Nothing+	, keyChunkSize = Nothing+	, keyChunkNum = Nothing+	}++-- Gets the parent of a chunk key.+nonChunkKey :: Key -> Key+nonChunkKey k = k+	{ keyChunkSize = Nothing+	, keyChunkNum = Nothing+	}++-- Where a chunk key is offset within its parent.+chunkKeyOffset :: Key -> Maybe Integer+chunkKeyOffset k = (*)+	<$> keyChunkSize k+	<*> (pred <$> keyChunkNum k)++isChunkKey :: Key -> Bool+isChunkKey k = isJust (keyChunkSize k) && isJust (keyChunkNum k)++-- Checks if a string looks like at least the start of a key.+isKeyPrefix :: String -> Bool+isKeyPrefix s = [fieldSep, fieldSep] `isInfixOf` s++fieldSep :: Char+fieldSep = '-'++{- Converts a key to a string that is suitable for use as a filename.+ - The name field is always shown last, separated by doubled fieldSeps,+ - and is the only field allowed to contain the fieldSep. -}+key2file :: Key -> FilePath+key2file Key { keyVariety = kv, keySize = s, keyMtime = m, keyChunkSize = cs, keyChunkNum = cn, keyName = n } =+	formatKeyVariety kv +++ ('s' ?: s) +++ ('m' ?: m) +++ ('S' ?: cs) +++ ('C' ?: cn) +++ (fieldSep : n)+  where+	"" +++ y = y+	x +++ "" = x+	x +++ y = x ++ fieldSep:y+	f ?: (Just v) = f : show v+	_ ?: _ = ""++file2key :: FilePath -> Maybe Key+file2key s+	| key == Just stubKey || (keyName <$> key) == Just "" || (keyVariety <$> key) == Just (OtherKey "") = Nothing+	| otherwise = key+  where+	key = startbackend stubKey s++	startbackend k v = sepfield k v addvariety+		+	sepfield k v a = case span (/= fieldSep) v of+		(v', _:r) -> findfields r $ a k v'+		_ -> Nothing++	findfields (c:v) (Just k)+		| c == fieldSep = addkeyname k v+		| otherwise = sepfield k v $ addfield c+	findfields _ v = v++	addvariety k v = Just k { keyVariety = parseKeyVariety v }++	-- This is a strict parser for security reasons; a key+	-- can contain only 4 fields, which all consist only of numbers.+	-- Any key containing other fields, or non-numeric data is+	-- rejected with Nothing.+	--+	-- If a key contained non-numeric fields, they could be used to+	-- embed data used in a SHA1 collision attack, which would be a+	-- problem since the keys are committed to git.+	addfield _ _ v | not (all isDigit v) = Nothing+	addfield 's' k v = do+		sz <- readish v+		return $ k { keySize = Just sz }+	addfield 'm' k v = do+		mtime <- readish v+		return $ k { keyMtime = Just mtime }+	addfield 'S' k v = do+		chunksize <- readish v+		return $ k { keyChunkSize = Just chunksize }+	addfield 'C' k v = case readish v of+		Just chunknum | chunknum > 0 ->+			return $ k { keyChunkNum = Just chunknum }+		_ -> Nothing+	addfield _ _ _ = Nothing++	addkeyname k v+		| validKeyName k v = Just $ k { keyName = v }+		| otherwise = Nothing++{- When a key HasExt, the length of the extension is limited in order to+ - mitigate against SHA1 collision attacks.+ -+ - In such an attack, the extension of the key could be made to contain+ - the collision generation data, with the result that a signed git commit+ - including such keys would not be secure.+ -+ - The maximum extension length ever generated for such a key was 8+ - characters; 20 is used here to give a little future wiggle-room. + - The SHA1 common-prefix attack needs 128 bytes of data.+ -}+validKeyName :: Key -> String -> Bool+validKeyName k name+	| hasExt (keyVariety k) = length (takeExtensions name) <= 20+	| otherwise = True++instance Arbitrary Key where+	arbitrary = Key+		<$> (listOf1 $ elements $ ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ "-_\r\n \t")+		<*> (parseKeyVariety <$> (listOf1 $ elements ['A'..'Z'])) -- BACKEND+		<*> ((abs <$>) <$> arbitrary) -- size cannot be negative+		<*> arbitrary+		<*> ((abs <$>) <$> arbitrary) -- chunksize cannot be negative+		<*> ((succ . abs <$>) <$> arbitrary) -- chunknum cannot be 0 or negative++instance Hashable Key where+	hashIO32 = hashIO32 . key2file+	hashIO64 = hashIO64 . key2file++instance ToJSON Key where+	toJSON = toJSON . key2file++instance FromJSON Key where+	parseJSON (String t) = maybe mempty pure $ file2key $ T.unpack t+	parseJSON _ = mempty++instance Proto.Serializable Key where+	serialize = key2file+	deserialize = file2key++prop_isomorphic_key_encode :: Key -> Bool+prop_isomorphic_key_encode k = Just k == (file2key . key2file) k++prop_isomorphic_key_decode :: FilePath -> Bool+prop_isomorphic_key_decode f+	| normalfieldorder = maybe True (\k -> key2file k == f) (file2key f)+	| otherwise = True+  where+	-- file2key will accept the fields in any order, so don't+	-- try the test unless the fields are in the normal order+	normalfieldorder = fields `isPrefixOf` "smSC"+	fields = map (f !!) $ filter (< length f) $ map succ $+		elemIndices fieldSep f
Limit.hs view
@@ -1,6 +1,6 @@ {- user-specified limits on files to act on  -- - Copyright 2011-2016 Joey Hess <id@joeyh.name>+ - Copyright 2011-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -19,6 +19,7 @@ import Annex.UUID import Logs.Trust import Annex.NumCopies+import Types.Key import Types.TrustLevel import Types.Group import Types.FileMatcher@@ -251,7 +252,15 @@ limitInBackend :: MkLimit Annex limitInBackend name = Right $ const $ checkKey check   where-	check key = pure $ keyBackendName key == name+	check key = pure $ keyVariety key == variety+	variety = parseKeyVariety name++{- Adds a limit to skip files not using a secure hash. -}+addSecureHash :: Annex ()+addSecureHash = addLimit $ Right limitSecureHash++limitSecureHash :: MatchFiles Annex+limitSecureHash _ = checkKey $ pure . cryptographicallySecure . keyVariety  {- Adds a limit to skip files that are too large or too small -} addLargerThan :: String -> Annex ()
Logs/Transfer.hs view
@@ -23,15 +23,6 @@ import Data.Time.Clock.POSIX import Control.Concurrent -showLcDirection :: Direction -> String-showLcDirection Upload = "upload"-showLcDirection Download = "download"--readLcDirection :: String -> Maybe Direction-readLcDirection "upload" = Just Upload-readLcDirection "download" = Just Download-readLcDirection _ = Nothing- describeTransfer :: Transfer -> TransferInfo -> String describeTransfer t info = unwords 	[ show $ transferDirection t@@ -212,7 +203,7 @@ 	| "lck." `isPrefixOf` takeFileName file = Nothing 	| otherwise = case drop (length bits - 3) bits of 		[direction, u, key] -> Transfer-			<$> readLcDirection direction+			<$> parseDirection direction 			<*> pure (toUUID u) 			<*> fileKey key 		_ -> Nothing@@ -279,14 +270,14 @@  {- The directory holding transfer information files for a given Direction. -} transferDir :: Direction -> Git.Repo -> FilePath-transferDir direction r = gitAnnexTransferDir r </> showLcDirection direction+transferDir direction r = gitAnnexTransferDir r </> formatDirection direction  {- The directory holding failed transfer information files for a given  - Direction and UUID -} failedTransferDir :: UUID -> Direction -> Git.Repo -> FilePath failedTransferDir u direction r = gitAnnexTransferDir r 	</> "failed"-	</> showLcDirection direction+	</> formatDirection direction 	</> filter (/= '/') (fromUUID u)  prop_read_write_transferinfo :: TransferInfo -> Bool
Messages/JSON.hs view
@@ -36,7 +36,7 @@ import Data.Monoid import Prelude -import Types.Key+import Key import Utility.Metered import Utility.Percentage 
NEWS view
@@ -1,3 +1,15 @@+git-annex (6.20170228) unstable; urgency=medium++  This version of git-annex has mitigations for SHA1 hash collision+  problems.+  +  A new annex.securehashesonly configuration, when used in combination with+  signed git commits, avoids potential hash collision problems in git-annex+  repositories. For details, see this web page:+  <https://git-annex.branchable.com/tips/using_signed_git_commits/>++ -- Joey Hess <id@joeyh.name>  Tue, 28 Feb 2017 13:28:50 -0400+ git-annex (6.20170101) unstable; urgency=medium    XMPP support has been removed from the assistant in this release.
Remote/Bup.hs view
@@ -28,6 +28,7 @@ import Utility.Hash import Utility.UserInfo import Annex.UUID+import Annex.Ssh import Utility.Metered  type BupRepo = String@@ -213,7 +214,7 @@ onBupRemote :: Git.Repo -> (FilePath -> [CommandParam] -> IO a) -> FilePath -> [CommandParam] -> Annex a onBupRemote r a command params = do 	c <- Annex.getRemoteGitConfig r-	sshparams <- Ssh.toRepo r c [Param $+	sshparams <- Ssh.toRepo NoConsumeStdin r c [Param $ 			"cd " ++ dir ++ " && " ++ unwords (command : toCommand params)] 	liftIO $ a "ssh" sshparams   where
Remote/Ddar.hs view
@@ -118,11 +118,11 @@  {- Return the command and parameters to use for a ddar call that may need to be  - made on a remote repository. This will call ssh if needed. -}-ddarRemoteCall :: DdarRepo -> Char -> [CommandParam] -> Annex (String, [CommandParam])-ddarRemoteCall ddarrepo cmd params+ddarRemoteCall :: ConsumeStdin -> DdarRepo -> Char -> [CommandParam] -> Annex (String, [CommandParam])+ddarRemoteCall cs ddarrepo cmd params 	| ddarLocal ddarrepo = return ("ddar", localParams) 	| otherwise = do-		os <- sshOptions (host, Nothing) (ddarRepoConfig ddarrepo) []+		os <- sshOptions cs (host, Nothing) (ddarRepoConfig ddarrepo) [] 		return ("ssh", os ++ remoteParams)   where 	(host, ddarrepo') = splitRemoteDdarRepo ddarrepo@@ -130,13 +130,13 @@ 	remoteParams = Param host : Param "ddar" : Param [cmd] : Param ddarrepo' : params  {- Specialized ddarRemoteCall that includes extraction command and flags -}-ddarExtractRemoteCall :: DdarRepo -> Key -> Annex (String, [CommandParam])-ddarExtractRemoteCall ddarrepo k =-	ddarRemoteCall ddarrepo 'x' [Param "--force-stdout", Param $ key2file k]+ddarExtractRemoteCall :: ConsumeStdin -> DdarRepo -> Key -> Annex (String, [CommandParam])+ddarExtractRemoteCall cs ddarrepo k =+	ddarRemoteCall cs ddarrepo 'x' [Param "--force-stdout", Param $ key2file k]  retrieve :: DdarRepo -> Retriever retrieve ddarrepo = byteRetriever $ \k sink -> do-	(cmd, params) <- ddarExtractRemoteCall ddarrepo k+	(cmd, params) <- ddarExtractRemoteCall NoConsumeStdin ddarrepo k 	let p = (proc cmd $ toCommand params) { std_out = CreatePipe } 	(_, Just h, _, pid) <- liftIO $ createProcess p 	liftIO (hClose h >> forceSuccessProcess p pid)@@ -147,7 +147,8 @@  remove :: DdarRepo -> Remover remove ddarrepo key = do-	(cmd, params) <- ddarRemoteCall ddarrepo 'd' [Param $ key2file key]+	(cmd, params) <- ddarRemoteCall NoConsumeStdin ddarrepo 'd'+		[Param $ key2file key] 	liftIO $ boolSystem cmd params  ddarDirectoryExists :: DdarRepo -> Annex (Either String Bool)@@ -158,7 +159,8 @@ 			Left _ -> Right False 			Right status -> Right $ isDirectory status 	| otherwise = do-		ps <- sshOptions (host, Nothing) (ddarRepoConfig ddarrepo) []+		ps <- sshOptions NoConsumeStdin (host, Nothing)+			(ddarRepoConfig ddarrepo) [] 		exitCode <- liftIO $ safeSystem "ssh" (ps ++ params) 		case exitCode of 			ExitSuccess -> return $ Right True@@ -178,7 +180,7 @@ {- Use "ddar t" to determine if a given key is present in a ddar archive -} inDdarManifest :: DdarRepo -> Key -> Annex (Either String Bool) inDdarManifest ddarrepo k = do-	(cmd, params) <- ddarRemoteCall ddarrepo 't' []+	(cmd, params) <- ddarRemoteCall NoConsumeStdin ddarrepo 't' [] 	let p = proc cmd $ toCommand params 	liftIO $ catchMsgIO $ withHandle StdoutHandle createProcessSuccess p $ \h -> do 		contents <- hGetContents h
Remote/GCrypt.hs view
@@ -159,7 +159,7 @@ 		let rsyncpath = if "/~/" `isPrefixOf` path 			then drop 3 path 			else path-		opts <- sshOptions (host, Nothing) gc []+		opts <- sshOptions ConsumeStdin (host, Nothing) gc [] 		return (rsyncShell $ Param "ssh" : opts, host ++ ":" ++ rsyncpath, AccessShell) 	othertransport = return ([], loc, AccessDirect) @@ -263,7 +263,8 @@  	{-  Ask git-annex-shell to configure the repository as a gcrypt 	 -  repository. May fail if it is too old. -}-	gitannexshellsetup = Ssh.onRemote r (boolSystem, return False)+	gitannexshellsetup = Ssh.onRemote NoConsumeStdin r+		(boolSystem, return False) 		"gcryptsetup" [ Param gcryptid ] []  	denyNonFastForwards = "receive.denyNonFastForwards"@@ -398,7 +399,7 @@ 	| Git.repoIsLocal r || Git.repoIsLocalUnknown r = extract <$> 		liftIO (catchMaybeIO $ Git.Config.read r) 	| not fast = extract . liftM fst <$> getM (eitherToMaybe <$>)-		[ Ssh.onRemote r (Git.Config.fromPipe r, return (Left $ error "configlist failed")) "configlist" [] []+		[ Ssh.onRemote NoConsumeStdin r (Git.Config.fromPipe r, return (Left $ error "configlist failed")) "configlist" [] [] 		, getConfigViaRsync r gc 		] 	| otherwise = return (Nothing, r)
Remote/Git.hs view
@@ -1,6 +1,6 @@ {- Standard git remotes.  -- - Copyright 2011-2015 Joey Hess <id@joeyh.name>+ - Copyright 2011-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -11,6 +11,7 @@ 	remote, 	configRead, 	repoAvail,+	onLocal, ) where  import Annex.Common@@ -54,9 +55,9 @@ import P2P.Address import Annex.Path import Creds-import Annex.CatFile import Messages.Progress import Types.NumCopies+import Annex.Concurrent  import Control.Concurrent import Control.Concurrent.MSampleVar@@ -210,7 +211,9 @@ tryGitConfigRead autoinit r  	| haveconfig r = return r -- already read 	| Git.repoIsSsh r = store $ do-		v <- Ssh.onRemote r (pipedconfig, return (Left $ giveup "configlist failed")) "configlist" [] configlistfields+		v <- Ssh.onRemote NoConsumeStdin r+			(pipedconfig, return (Left $ giveup "configlist failed"))+			"configlist" [] configlistfields 		case v of 			Right r' 				| haveconfig r' -> return r'@@ -334,7 +337,7 @@ 	checkremote = Ssh.inAnnex r key 	checklocal = guardUsable r (cantCheck r) $ 		maybe (cantCheck r) return-			=<< onLocal rmt (Annex.Content.inAnnexSafe key)+			=<< onLocalFast rmt (Annex.Content.inAnnexSafe key)  keyUrls :: Remote -> Key -> [String] keyUrls r key = map tourl locs'@@ -357,7 +360,7 @@ dropKey r key 	| not $ Git.repoIsUrl (repo r) = 		guardUsable (repo r) (return False) $-			commitOnCleanup r $ onLocal r $ do+			commitOnCleanup r $ onLocalFast r $ do 				ensureInitialized 				whenM (Annex.Content.inAnnex key) $ do 					Annex.Content.lockContentForRemoval key $ \lock -> do@@ -376,7 +379,7 @@ 			-- Lock content from perspective of remote, 			-- and then run the callback in the original 			-- annex monad, not the remote's.-			onLocal r $ +			onLocalFast r $  				Annex.Content.lockContentShared key $ \vc -> 					ifM (Annex.Content.inAnnex key) 						( liftIO $ inorigrepo $ callback vc@@ -384,7 +387,8 @@ 						) 	| Git.repoIsSsh (repo r) = do 		showLocking r-		Just (cmd, params) <- Ssh.git_annex_shell (repo r) "lockcontent"+		Just (cmd, params) <- Ssh.git_annex_shell ConsumeStdin+			(repo r) "lockcontent" 			[Param $ key2file key] [] 		(Just hin, Just hout, Nothing, p) <- liftIO $  			withFile devNull WriteMode $ \nullh ->@@ -439,7 +443,7 @@ 		u <- getUUID 		hardlink <- wantHardLink 		-- run copy from perspective of remote-		onLocal r $ do+		onLocalFast r $ do 			ensureInitialized 			v <- Annex.Content.prepSendAnnex key 			case v of@@ -477,7 +481,8 @@ 		u <- getUUID 		let fields = (Fields.remoteUUID, fromUUID u) 			: maybe [] (\f -> [(Fields.associatedFile, f)]) file-		Just (cmd, params) <- Ssh.git_annex_shell (repo r) "transferinfo" +		Just (cmd, params) <- Ssh.git_annex_shell ConsumeStdin+			(repo r) "transferinfo"  			[Param $ key2file key] fields 		v <- liftIO (newEmptySV :: IO (MSampleVar Integer)) 		pidv <- liftIO $ newEmptyMVar@@ -567,7 +572,7 @@ 		u <- getUUID 		hardlink <- wantHardLink 		-- run copy from perspective of remote-		onLocal r $ ifM (Annex.Content.inAnnex key)+		onLocalFast r $ ifM (Annex.Content.inAnnex key) 			( return True 			, do 				ensureInitialized@@ -583,7 +588,7 @@ fsckOnRemote :: Git.Repo -> [CommandParam] -> Annex (IO Bool) fsckOnRemote r params 	| Git.repoIsUrl r = do-		s <- Ssh.git_annex_shell r "fsck" params []+		s <- Ssh.git_annex_shell NoConsumeStdin r "fsck" params [] 		return $ case s of 			Nothing -> return False 			Just (c, ps) -> batchCommand c ps@@ -609,34 +614,36 @@ {- Runs an action from the perspective of a local remote.  -  - The AnnexState is cached for speed and to avoid resource leaks.- - However, catFileStop is called to avoid git-cat-file processes hanging- - around on removable media.- -- - The repository's git-annex branch is not updated, as an optimisation.- - No caller of onLocal can query data from the branch and be ensured- - it gets a current value. Caller of onLocal can make changes to- - the branch, however.+ - However, coprocesses are stopped after each call to avoid git+ - processes hanging around on removable media.  -} onLocal :: Remote -> Annex a -> Annex a onLocal r a = do 	m <- Annex.getState Annex.remoteannexstate-	case M.lookup (uuid r) m of-		Nothing -> do-			st <- liftIO $ Annex.new (repo r)-			go st $ do-				Annex.BranchState.disableUpdate	-				a-		Just st -> go st a+	go =<< maybe+		(liftIO $ Annex.new $ repo r)+		return+		(M.lookup (uuid r) m)   where 	cache st = Annex.changeState $ \s -> s 		{ Annex.remoteannexstate = M.insert (uuid r) st (Annex.remoteannexstate s) }-	go st a' = do+	go st = do 		curro <- Annex.getState Annex.output 		(ret, st') <- liftIO $ Annex.run (st { Annex.output = curro }) $-			catFileStop `after` a'+			stopCoProcesses `after` a 		cache st' 		return ret +{- Faster variant of onLocal.+ -+ - The repository's git-annex branch is not updated, as an optimisation.+ - No caller of onLocalFast can query data from the branch and be ensured+ - it gets the most current value. Caller of onLocalFast can make changes+ - to the branch, however.+ -}+onLocalFast :: Remote -> Annex a -> Annex a+onLocalFast r a = onLocal r $ Annex.BranchState.disableUpdate >> a+ {- Copys a file with rsync unless both locations are on the same  - filesystem. Then cp could be faster. -} rsyncOrCopyFile :: [CommandParam] -> FilePath -> FilePath -> MeterUpdate -> Annex Bool@@ -660,12 +667,13 @@   where 	go = Annex.addCleanup (RemoteCleanup $ uuid r) cleanup 	cleanup-		| not $ Git.repoIsUrl (repo r) = onLocal r $+		| not $ Git.repoIsUrl (repo r) = onLocalFast r $ 			doQuietSideAction $ 				Annex.Branch.commit "update" 		| otherwise = void $ do 			Just (shellcmd, shellparams) <--				Ssh.git_annex_shell (repo r) "commit" [] []+				Ssh.git_annex_shell NoConsumeStdin+					(repo r) "commit" [] [] 			 			-- Throw away stderr, since the remote may not 			-- have a new enough git-annex shell to
Remote/Helper/Ssh.hs view
@@ -26,17 +26,17 @@ {- Generates parameters to ssh to a repository's host and run a command.  - Caller is responsible for doing any neccessary shellEscaping of the  - passed command. -}-toRepo :: Git.Repo -> RemoteGitConfig -> [CommandParam] -> Annex [CommandParam]-toRepo r gc sshcmd = do+toRepo :: ConsumeStdin -> Git.Repo -> RemoteGitConfig -> [CommandParam] -> Annex [CommandParam]+toRepo cs r gc sshcmd = do 	let opts = map Param $ remoteAnnexSshOptions gc 	let host = fromMaybe (giveup "bad ssh url") $ Git.Url.hostuser r-	params <- sshOptions (host, Git.Url.port r) gc opts+	params <- sshOptions cs (host, Git.Url.port r) gc opts 	return $ params ++ Param host : sshcmd  {- Generates parameters to run a git-annex-shell command on a remote  - repository. -}-git_annex_shell :: Git.Repo -> String -> [CommandParam] -> [(Field, String)] -> Annex (Maybe (FilePath, [CommandParam]))-git_annex_shell r command params fields+git_annex_shell :: ConsumeStdin -> Git.Repo -> String -> [CommandParam] -> [(Field, String)] -> Annex (Maybe (FilePath, [CommandParam]))+git_annex_shell cs r command params fields 	| not $ Git.repoIsUrl r = do 		shellopts <- getshellopts 		return $ Just (shellcmd, shellopts ++ fieldopts)@@ -49,7 +49,7 @@ 				: map shellEscape (toCommand shellopts) ++ 			uuidcheck u ++ 			map shellEscape (toCommand fieldopts)-		sshparams <- toRepo r gc [Param sshcmd]+		sshparams <- toRepo cs r gc [Param sshcmd] 		return $ Just ("ssh", sshparams) 	| otherwise = return Nothing   where@@ -76,14 +76,15 @@  - Or, if the remote does not support running remote commands, returns  - a specified error value. -} onRemote -	:: Git.Repo+	:: ConsumeStdin+	-> Git.Repo 	-> (FilePath -> [CommandParam] -> IO a, Annex a) 	-> String 	-> [CommandParam] 	-> [(Field, String)] 	-> Annex a-onRemote r (with, errorval) command params fields = do-	s <- git_annex_shell r command params fields+onRemote cs r (with, errorval) command params fields = do+	s <- git_annex_shell cs r command params fields 	case s of 		Just (c, ps) -> liftIO $ with c ps 		Nothing -> errorval@@ -92,7 +93,7 @@ inAnnex :: Git.Repo -> Key -> Annex Bool inAnnex r k = do 	showChecking r-	onRemote r (check, cantCheck r) "inannex" [Param $ key2file k] []+	onRemote NoConsumeStdin r (check, cantCheck r) "inannex" [Param $ key2file k] []   where 	check c p = dispatch =<< safeSystem c p 	dispatch ExitSuccess = return True@@ -101,7 +102,7 @@  {- Removes a key from a remote. -} dropKey :: Git.Repo -> Key -> Annex Bool-dropKey r key = onRemote r (boolSystem, return False) "dropkey"+dropKey r key = onRemote NoConsumeStdin r (boolSystem, return False) "dropkey" 	[ Param "--quiet", Param "--force" 	, Param $ key2file key 	]@@ -133,7 +134,7 @@ 		-- compatability. 		: (Fields.direct, if unlocked then "1" else "") 		: maybe [] (\f -> [(Fields.associatedFile, f)]) afile-	Just (shellcmd, shellparams) <- git_annex_shell (repo r)+	Just (shellcmd, shellparams) <- git_annex_shell ConsumeStdin (repo r) 		(if direction == Download then "sendkey" else "recvkey") 		[ Param $ key2file key ] 		fields
Remote/Rsync.hs view
@@ -122,7 +122,7 @@ 				let (port, sshopts') = sshReadPort sshopts 				    userhost = takeWhile (/=':') url 				-- Connection caching-				(Param "ssh":) <$> sshOptions+				(Param "ssh":) <$> sshOptions ConsumeStdin 					(userhost, port) gc 					(map Param $ loginopt ++ sshopts') 			"rsh":rshopts -> return $ map Param $ "rsh" :
RemoteDaemon/Transport/GCrypt.hs view
@@ -14,12 +14,13 @@ import Git.GCrypt import Remote.Helper.Ssh import Remote.GCrypt (accessShellConfig)+import Annex.Ssh  transport :: Transport transport rr@(RemoteRepo r gc) url h@(TransportHandle (LocalRepo g) _) ichan ochan 	| accessShellConfig gc = do 		r' <- encryptedRemote g r-		v <- liftAnnex h $ git_annex_shell r' "notifychanges" [] []+		v <- liftAnnex h $ git_annex_shell ConsumeStdin r' "notifychanges" [] [] 		case v of 			Nothing -> noop 			Just (cmd, params) -> 
RemoteDaemon/Transport/Ssh.hs view
@@ -23,7 +23,7 @@  transport :: Transport transport rr@(RemoteRepo r _) url h ichan ochan = do-	v <- liftAnnex h $ git_annex_shell r "notifychanges" [] []+	v <- liftAnnex h $ git_annex_shell ConsumeStdin r "notifychanges" [] [] 	case v of 		Nothing -> noop 		Just (cmd, params) -> transportUsingCmd cmd params rr url h ichan ochan
Test.hs view
@@ -63,6 +63,7 @@ import qualified Logs.PreferredContent import qualified Types.MetaData import qualified Remote+import qualified Key import qualified Types.Key import qualified Types.Messages import qualified Config@@ -152,8 +153,8 @@ 	[ testProperty "prop_isomorphic_deencode_git" Git.Filename.prop_isomorphic_deencode 	, testProperty "prop_isomorphic_deencode" Utility.Format.prop_isomorphic_deencode 	, testProperty "prop_isomorphic_fileKey" Annex.Locations.prop_isomorphic_fileKey-	, testProperty "prop_isomorphic_key_encode" Types.Key.prop_isomorphic_key_encode-	, testProperty "prop_isomorphic_key_decode" Types.Key.prop_isomorphic_key_decode+	, testProperty "prop_isomorphic_key_encode" Key.prop_isomorphic_key_encode+	, testProperty "prop_isomorphic_key_decode" Key.prop_isomorphic_key_decode 	, testProperty "prop_isomorphic_shellEscape" Utility.SafeCommand.prop_isomorphic_shellEscape 	, testProperty "prop_isomorphic_shellEscape_multiword" Utility.SafeCommand.prop_isomorphic_shellEscape_multiword 	, testProperty "prop_isomorphic_configEscape" Logs.Remote.prop_isomorphic_configEscape@@ -390,7 +391,7 @@ 	git_annex "drop" ["--force", sha1annexedfile] @? "drop failed" 	annexed_notpresent sha1annexedfile 	writeFile tmp $ content sha1annexedfile-	key <- Types.Key.key2file <$> getKey backendSHA1 tmp+	key <- Key.key2file <$> getKey backendSHA1 tmp 	git_annex "reinject" [tmp, sha1annexedfile] @? "reinject failed" 	annexed_present sha1annexedfile 	-- fromkey can't be used on a crippled filesystem, since it makes a@@ -846,9 +847,9 @@ 	checkunused [annexedfilekey, sha1annexedfilekey] "after rm sha1annexedfile"  	-- good opportunity to test dropkey also-	git_annex "dropkey" ["--force", Types.Key.key2file annexedfilekey]+	git_annex "dropkey" ["--force", Key.key2file annexedfilekey] 		@? "dropkey failed"-	checkunused [sha1annexedfilekey] ("after dropkey --force " ++ Types.Key.key2file annexedfilekey)+	checkunused [sha1annexedfilekey] ("after dropkey --force " ++ Key.key2file annexedfilekey)  	not <$> git_annex "dropunused" ["1"] @? "dropunused failed to fail without --force" 	git_annex "dropunused" ["--force", "1"] @? "dropunused failed"@@ -1959,7 +1960,7 @@ 	case r of 		Just k -> do 			uuids <- annexeval $ Remote.keyLocations k-			assertEqual ("bad content in location log for " ++ f ++ " key " ++ Types.Key.key2file k ++ " uuid " ++ show thisuuid)+			assertEqual ("bad content in location log for " ++ f ++ " key " ++ Key.key2file k ++ " uuid " ++ show thisuuid) 				expected (thisuuid `elem` uuids) 		_ -> assertFailure $ f ++ " failed to look up key" @@ -2152,7 +2153,7 @@ backendWORM = backend_ "WORM"  backend_ :: String -> Types.Backend-backend_ = Backend.lookupBackendName+backend_ = Backend.lookupBackendVariety . Types.Key.parseKeyVariety  getKey :: Types.Backend -> FilePath -> IO Types.Key getKey b f = fromJust <$> annexeval go
Types/ActionItem.hs view
@@ -9,7 +9,7 @@  module Types.ActionItem where -import Types.Key+import Key import Types.Transfer import Git.FilePath 
Types/Backend.hs view
@@ -2,7 +2,7 @@  -  - Most things should not need this, using Types instead  -- - Copyright 2010,2012 Joey Hess <id@joeyh.name>+ - Copyright 2010-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -13,7 +13,7 @@ import Types.KeySource  data BackendA a = Backend-	{ name :: String+	{ backendVariety :: KeyVariety 	, getKey :: KeySource -> a (Maybe Key)  	-- Verifies the content of a key. 	, verifyKeyContent :: Maybe (Key -> FilePath -> a Bool)@@ -28,7 +28,7 @@ 	}  instance Show (BackendA a) where-	show backend = "Backend { name =\"" ++ name backend ++ "\" }"+	show backend = "Backend { name =\"" ++ formatKeyVariety (backendVariety backend) ++ "\" }"  instance Eq (BackendA a) where-	a == b = name a == name b+	a == b = backendVariety a == backendVariety b
Types/Distribution.hs view
@@ -1,16 +1,24 @@ {- Data type for a distribution of git-annex  -- - Copyright 2013 Joey Hess <id@joeyh.name>+ - Copyright 2013, 2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -}  module Types.Distribution where +import Utility.PartialPrelude import Types.Key+import Key import Data.Time.Clock import Git.Config (isTrue, boolConfig) +import Data.String.Utils+import Control.Applicative+import Prelude++type GitAnnexVersion = String+ data GitAnnexDistribution = GitAnnexDistribution 	{ distributionUrl :: String 	, distributionKey :: Key@@ -20,7 +28,39 @@ 	} 	deriving (Read, Show, Eq) -type GitAnnexVersion = String+{- The first line of the info file is in the format old versions of+ - git-annex expect to read a GitAnnexDistribution.+ - The remainder of the file is in the new format.+ - This works because old versions of git-annex used readish to parse+ - the file, and that ignores the second line.+ -}+formatInfoFile :: GitAnnexDistribution -> String+formatInfoFile d = replace "keyVariant = " "keyBackendName = " (show d) +++	"\n" ++ formatGitAnnexDistribution d++parseInfoFile :: String -> Maybe GitAnnexDistribution+parseInfoFile s = case lines s of+	(_oldformat:rest) -> parseGitAnnexDistribution (unlines rest)+	_ -> Nothing++formatGitAnnexDistribution :: GitAnnexDistribution -> String+formatGitAnnexDistribution d = unlines+	[ distributionUrl d+	, key2file (distributionKey d)+	, distributionVersion d+	, show (distributionReleasedate d)+	, maybe "" show (distributionUrgentUpgrade d)+	]++parseGitAnnexDistribution :: String -> Maybe GitAnnexDistribution+parseGitAnnexDistribution s = case lines s of+	(u:k:v:d:uu:_) -> GitAnnexDistribution+		<$> pure u+		<*> file2key k+		<*> pure v+		<*> readish d+		<*> pure (readish uu)+	_ -> Nothing  data AutoUpgrade = AskUpgrade | AutoUpgrade | NoAutoUpgrade 	deriving (Eq)
Types/GitConfig.hs view
@@ -18,7 +18,7 @@ import qualified Git import qualified Git.Config import qualified Git.Construct-import Git.SharedRepository+import Git.ConfigTypes import Utility.DataUnits import Config.Cost import Types.UUID@@ -82,8 +82,10 @@ 	, annexPidLock :: Bool 	, annexPidLockTimeout :: Seconds 	, annexAddUnlocked :: Bool+	, annexSecureHashesOnly :: Bool 	, coreSymlinks :: Bool 	, coreSharedRepository :: SharedRepository+	, receiveDenyCurrentBranch :: DenyCurrentBranch 	, gcryptId :: Maybe String 	, gpgCmd :: GpgCmd 	}@@ -135,8 +137,10 @@ 	, annexPidLockTimeout = Seconds $ fromMaybe 300 $ 		getmayberead (annex "pidlocktimeout") 	, annexAddUnlocked = getbool (annex "addunlocked") False+	, annexSecureHashesOnly = getbool (annex "securehashesonly") False 	, coreSymlinks = getbool "core.symlinks" True 	, coreSharedRepository = getSharedRepository r+	, receiveDenyCurrentBranch = getDenyCurrentBranch r 	, gcryptId = getmaybe "core.gcrypt-id" 	, gpgCmd = mkGpgCmd (getmaybe "gpg.program") 	}
Types/Key.hs view
@@ -1,39 +1,21 @@ {- git-annex Key data type  -- - Copyright 2011-2016 Joey Hess <id@joeyh.name>+ - Copyright 2011-2017 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU GPL version 3 or higher.  -} -module Types.Key (-	Key(..),-	AssociatedFile,-	stubKey,-	key2file,-	file2key,-	nonChunkKey,-	chunkKeyOffset,-	isChunkKey,-	isKeyPrefix,+module Types.Key where -	prop_isomorphic_key_encode,-	prop_isomorphic_key_decode-) where+import Utility.PartialPrelude  import System.Posix.Types-import Data.Aeson-import qualified Data.Text as T -import Common-import Utility.QuickCheck-import Utility.Bloom-import qualified Utility.SimpleProtocol as Proto- {- A Key has a unique name, which is derived from a particular backend,  - and may contain other optional metadata. -} data Key = Key 	{ keyName :: String-	, keyBackendName :: String+	, keyVariety :: KeyVariety 	, keySize :: Maybe Integer 	, keyMtime :: Maybe EpochTime 	, keyChunkSize :: Maybe Integer@@ -43,120 +25,92 @@ {- A filename may be associated with a Key. -} type AssociatedFile = Maybe FilePath -stubKey :: Key-stubKey = Key-	{ keyName = ""-	, keyBackendName = ""-	, keySize = Nothing-	, keyMtime = Nothing-	, keyChunkSize = Nothing-	, keyChunkNum = Nothing-	}---- Gets the parent of a chunk key.-nonChunkKey :: Key -> Key-nonChunkKey k = k-	{ keyChunkSize = Nothing-	, keyChunkNum = Nothing-	}+{- There are several different varieties of keys. -}+data KeyVariety+	= SHA2Key HashSize HasExt+	| SHA3Key HashSize HasExt+	| SKEINKey HashSize HasExt+	| SHA1Key HasExt+	| MD5Key HasExt+	| WORMKey+	| URLKey+ 	-- Some repositories may contain keys of other varieties,+	-- which can still be processed to some extent.+	| OtherKey String+	deriving (Eq, Ord, Read, Show) --- Where a chunk key is offset within its parent.-chunkKeyOffset :: Key -> Maybe Integer-chunkKeyOffset k = (*)-	<$> keyChunkSize k-	<*> (pred <$> keyChunkNum k)+{- Some varieties of keys may contain an extension at the end of the+ - keyName -}+newtype HasExt = HasExt Bool+	deriving (Eq, Ord, Read, Show) -isChunkKey :: Key -> Bool-isChunkKey k = isJust (keyChunkSize k) && isJust (keyChunkNum k)+newtype HashSize = HashSize Int+	deriving (Eq, Ord, Read, Show) --- Checks if a string looks like at least the start of a key.-isKeyPrefix :: String -> Bool-isKeyPrefix s = [fieldSep, fieldSep] `isInfixOf` s+hasExt :: KeyVariety -> Bool+hasExt (SHA2Key _ (HasExt b)) = b+hasExt (SHA3Key _ (HasExt b)) = b+hasExt (SKEINKey _ (HasExt b)) = b+hasExt (SHA1Key (HasExt b)) = b+hasExt (MD5Key (HasExt b)) = b+hasExt WORMKey = False+hasExt URLKey = False+hasExt (OtherKey s) =  end s == "E" -fieldSep :: Char-fieldSep = '-'+sameExceptExt :: KeyVariety -> KeyVariety -> Bool+sameExceptExt (SHA2Key sz1 _) (SHA2Key sz2 _) = sz1 == sz2+sameExceptExt (SHA3Key sz1 _) (SHA3Key sz2 _) = sz1 == sz2+sameExceptExt (SKEINKey sz1 _) (SKEINKey sz2 _) = sz1 == sz2+sameExceptExt (SHA1Key _) (SHA1Key _) = True+sameExceptExt (MD5Key _) (MD5Key _) = True+sameExceptExt _ _ = False -{- Converts a key to a string that is suitable for use as a filename.- - The name field is always shown last, separated by doubled fieldSeps,- - and is the only field allowed to contain the fieldSep. -}-key2file :: Key -> FilePath-key2file Key { keyBackendName = b, keySize = s, keyMtime = m, keyChunkSize = cs, keyChunkNum = cn, keyName = n } =-	b +++ ('s' ?: s) +++ ('m' ?: m) +++ ('S' ?: cs) +++ ('C' ?: cn) +++ (fieldSep : n)-  where-	"" +++ y = y-	x +++ "" = x-	x +++ y = x ++ fieldSep:y-	f ?: (Just v) = f : show v-	_ ?: _ = ""+{- Is the Key variety cryptographically secure, such that no two differing+ - file contents can be mapped to the same Key? -}+cryptographicallySecure :: KeyVariety -> Bool+cryptographicallySecure (SHA2Key _ _) = True+cryptographicallySecure (SHA3Key _ _) = True+cryptographicallySecure (SKEINKey _ _) = True+cryptographicallySecure _ = False -file2key :: FilePath -> Maybe Key-file2key s-	| key == Just stubKey || (keyName <$> key) == Just "" || (keyBackendName <$> key) == Just "" = Nothing-	| otherwise = key+formatKeyVariety :: KeyVariety -> String+formatKeyVariety v = case v of+	SHA2Key sz e -> adde e (addsz sz "SHA")+	SHA3Key sz e -> adde e (addsz sz "SHA3_")+	SKEINKey sz e -> adde e (addsz sz "SKEIN")+	SHA1Key e -> adde e "SHA1"+	MD5Key e -> adde e "MD5"+	WORMKey -> "WORM"+	URLKey -> "URL"+	OtherKey s -> s   where-	key = startbackend stubKey s--	startbackend k v = sepfield k v addbackend-		-	sepfield k v a = case span (/= fieldSep) v of-		(v', _:r) -> findfields r $ a k v'-		_ -> Nothing--	findfields (c:v) (Just k)-		| c == fieldSep = Just $ k { keyName = v }-		| otherwise = sepfield k v $ addfield c-	findfields _ v = v--	addbackend k v = Just k { keyBackendName = v }-	addfield 's' k v = do-		sz <- readish v-		return $ k { keySize = Just sz }-	addfield 'm' k v = do-		mtime <- readish v-		return $ k { keyMtime = Just mtime }-	addfield 'S' k v = do-		chunksize <- readish v-		return $ k { keyChunkSize = Just chunksize }-	addfield 'C' k v = case readish v of-		Just chunknum | chunknum > 0 ->-			return $ k { keyChunkNum = Just chunknum }-		_ -> return k-	addfield _ _ _ = Nothing--instance ToJSON Key where-	toJSON = toJSON . key2file--instance FromJSON Key where-	parseJSON (String t) = maybe mempty pure $ file2key $ T.unpack t-	parseJSON _ = mempty--instance Proto.Serializable Key where-	serialize = key2file-	deserialize = file2key--instance Arbitrary Key where-	arbitrary = Key-		<$> (listOf1 $ elements $ ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ "-_\r\n \t")-		<*> (listOf1 $ elements ['A'..'Z']) -- BACKEND-		<*> ((abs <$>) <$> arbitrary) -- size cannot be negative-		<*> arbitrary-		<*> ((abs <$>) <$> arbitrary) -- chunksize cannot be negative-		<*> ((succ . abs <$>) <$> arbitrary) -- chunknum cannot be 0 or negative--instance Hashable Key where-	hashIO32 = hashIO32 . key2file-	hashIO64 = hashIO64 . key2file--prop_isomorphic_key_encode :: Key -> Bool-prop_isomorphic_key_encode k = Just k == (file2key . key2file) k+	adde (HasExt False) s = s+	adde (HasExt True) s = s ++ "E"+	addsz (HashSize n) s =  s ++ show n -prop_isomorphic_key_decode :: FilePath -> Bool-prop_isomorphic_key_decode f-	| normalfieldorder = maybe True (\k -> key2file k == f) (file2key f)-	| otherwise = True-  where-	-- file2key will accept the fields in any order, so don't-	-- try the test unless the fields are in the normal order-	normalfieldorder = fields `isPrefixOf` "smSC"-	fields = map (f !!) $ filter (< length f) $ map succ $-		elemIndices fieldSep f+parseKeyVariety :: String -> KeyVariety+parseKeyVariety "SHA256"    =  SHA2Key (HashSize 256) (HasExt False)+parseKeyVariety "SHA256E"   = SHA2Key (HashSize 256) (HasExt True)+parseKeyVariety "SHA512"    = SHA2Key (HashSize 512) (HasExt False)+parseKeyVariety "SHA512E"   = SHA2Key (HashSize 512) (HasExt True)+parseKeyVariety "SHA224"    = SHA2Key (HashSize 224) (HasExt False)+parseKeyVariety "SHA224E"   = SHA2Key (HashSize 224) (HasExt True)+parseKeyVariety "SHA384"    = SHA2Key (HashSize 384) (HasExt False)+parseKeyVariety "SHA384E"   = SHA2Key (HashSize 384) (HasExt True)+parseKeyVariety "SHA3_512"  = SHA3Key (HashSize 512) (HasExt False)+parseKeyVariety "SHA3_512E" = SHA3Key (HashSize 512) (HasExt True)+parseKeyVariety "SHA3_384"  = SHA3Key (HashSize 384) (HasExt False)+parseKeyVariety "SHA3_384E" = SHA3Key (HashSize 384) (HasExt True)+parseKeyVariety "SHA3_256"  = SHA3Key (HashSize 256) (HasExt False)+parseKeyVariety "SHA3_256E" = SHA3Key (HashSize 256) (HasExt True)+parseKeyVariety "SHA3_224"  = SHA3Key (HashSize 224) (HasExt False)+parseKeyVariety "SHA3_224E" = SHA3Key (HashSize 224) (HasExt True)+parseKeyVariety "SKEIN512"  = SKEINKey (HashSize 512) (HasExt False)+parseKeyVariety "SKEIN512E" = SKEINKey (HashSize 512) (HasExt True)+parseKeyVariety "SKEIN256"  = SKEINKey (HashSize 256) (HasExt False)+parseKeyVariety "SKEIN256E" = SKEINKey (HashSize 256) (HasExt True)+parseKeyVariety "SHA1"      = SHA1Key (HasExt False)+parseKeyVariety "MD5"       = MD5Key (HasExt False)+parseKeyVariety "WORM"      = WORMKey+parseKeyVariety "URL"       = URLKey+parseKeyVariety s           = OtherKey s
Types/Transfer.hs view
@@ -16,8 +16,7 @@ import Control.Applicative import Prelude -{- Enough information to uniquely identify a transfer, used as the filename- - of the transfer information file. -}+{- Enough information to uniquely identify a transfer. -} data Transfer = Transfer 	{ transferDirection :: Direction 	, transferUUID :: UUID@@ -46,7 +45,16 @@ stubTransferInfo = TransferInfo Nothing Nothing Nothing Nothing Nothing Nothing False  data Direction = Upload | Download-	deriving (Eq, Ord, Read, Show)+	deriving (Eq, Ord, Show, Read)++formatDirection :: Direction -> String+formatDirection Upload = "upload"+formatDirection Download = "download"++parseDirection :: String -> Maybe Direction+parseDirection "upload" = Just Upload+parseDirection "download" = Just Download+parseDirection _ = Nothing  instance Arbitrary TransferInfo where 	arbitrary = TransferInfo
Upgrade/V1.hs view
@@ -14,6 +14,7 @@ import Annex.Common import Annex.Content import Annex.Link+import Types.Key import Logs.Presence import qualified Annex.Queue import qualified Git@@ -73,7 +74,7 @@ 		let d = parentDir f 		liftIO $ allowWrite d 		liftIO $ allowWrite f-		moveAnnex k f+		_ <- moveAnnex k f 		liftIO $ removeDirectory d  updateSymlinks :: Annex ()@@ -130,7 +131,7 @@   where 	len = length l - 4 	k = readKey1 (take len l)-	sane = (not . null $ keyName k) && (not . null $ keyBackendName k)+	sane = (not . null $ keyName k) && (not . null $ formatKeyVariety $ keyVariety k)  -- WORM backend keys: "WORM:mtime:size:filename" -- all the rest: "backend:key"@@ -143,7 +144,7 @@ 	| mixup = fromJust $ file2key $ intercalate ":" $ Prelude.tail bits 	| otherwise = stubKey 		{ keyName = n-		, keyBackendName = b+		, keyVariety = parseKeyVariety b 		, keySize = s 		, keyMtime = t 		}@@ -161,11 +162,12 @@ 	mixup = wormy && isUpper (Prelude.head $ bits !! 1)  showKey1 :: Key -> String-showKey1 Key { keyName = n , keyBackendName = b, keySize = s, keyMtime = t } =+showKey1 Key { keyName = n , keyVariety = v, keySize = s, keyMtime = t } = 	intercalate ":" $ filter (not . null) [b, showifhere t, showifhere s, n]   where 	showifhere Nothing = ""-	showifhere (Just v) = show v+	showifhere (Just x) = show x+	b = formatKeyVariety v  keyFile1 :: Key -> FilePath keyFile1 key = replace "/" "%" $ replace "%" "&s" $ replace "&" "&a"  $ showKey1 key@@ -189,7 +191,7 @@ 		Right l -> makekey l   where 	getsymlink = takeFileName <$> readSymbolicLink file-	makekey l = case maybeLookupBackendName bname of+	makekey l = case maybeLookupBackendVariety (keyVariety k) of 		Nothing -> do 			unless (null kname || null bname || 			        not (isLinkToAnnex l)) $@@ -198,7 +200,7 @@ 		Just backend -> return $ Just (k, backend) 	  where 		k = fileKey1 l-		bname = keyBackendName k+		bname = formatKeyVariety (keyVariety k) 		kname = keyName k 		skip = "skipping " ++ file ++  			" (unknown backend " ++ bname ++ ")"
Utility/Hash.hs view
@@ -4,20 +4,16 @@  - because of https://github.com/vincenthz/hs-cryptohash/issues/36  -} -{-# LANGUAGE CPP #-}- module Utility.Hash ( 	sha1, 	sha2_224, 	sha2_256, 	sha2_384, 	sha2_512,-#ifdef WITH_CRYPTONITE 	sha3_224, 	sha3_256, 	sha3_384, 	sha3_512,-#endif 	skein256, 	skein512, 	md5,@@ -31,12 +27,8 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.ByteString as S-#ifdef WITH_CRYPTONITE import "cryptonite" Crypto.MAC.HMAC import "cryptonite" Crypto.Hash-#else-import "cryptohash" Crypto.Hash-#endif  sha1 :: L.ByteString -> Digest SHA1 sha1 = hashlazy@@ -53,7 +45,6 @@ sha2_512 :: L.ByteString -> Digest SHA512 sha2_512 = hashlazy -#ifdef WITH_CRYPTONITE sha3_224 :: L.ByteString -> Digest SHA3_224 sha3_224 = hashlazy @@ -65,7 +56,6 @@  sha3_512 :: L.ByteString -> Digest SHA3_512 sha3_512 = hashlazy-#endif  skein256 :: L.ByteString -> Digest Skein256_256 skein256 = hashlazy@@ -86,12 +76,10 @@ 	, (show . sha2_512, "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7") 	, (show . skein256, "a04efd9a0aeed6ede40fe5ce0d9361ae7b7d88b524aa19917b9315f1ecf00d33") 	, (show . skein512, "fd8956898113510180aa4658e6c0ac85bd74fb47f4a4ba264a6b705d7a8e8526756e75aecda12cff4f1aca1a4c2830fbf57f458012a66b2b15a3dd7d251690a7")-#ifdef WITH_CRYPTONITE 	, (show . sha3_224, "f4f6779e153c391bbd29c95e72b0708e39d9166c7cea51d1f10ef58a") 	, (show . sha3_256, "76d3bc41c9f588f7fcd0d5bf4718f8f84b1c41b20882703100b9eb9413807c01") 	, (show . sha3_384, "665551928d13b7d84ee02734502b018d896a0fb87eed5adb4c87ba91bbd6489410e11b0fbcc06ed7d0ebad559e5d3bb5") 	, (show . sha3_512, "4bca2b137edc580fe50a88983ef860ebaca36c857b1f492839d6d7392452a63c82cbebc68e3b70a2a1480b4bb5d437a7cba6ecf9d89f9ff3ccd14cd6146ea7e7")-#endif 	, (show . md5, "acbd18db4cc2f85cedef654fccc4a4d8") 	]   where
Utility/Process.hs view
@@ -174,22 +174,21 @@ -- returns a transcript combining its stdout and stderr, and -- whether it succeeded or failed. processTranscript :: String -> [String] -> (Maybe String) -> IO (String, Bool)-processTranscript = processTranscript' id+processTranscript cmd opts = processTranscript' (proc cmd opts) -processTranscript' :: (CreateProcess -> CreateProcess) -> String -> [String] -> Maybe String -> IO (String, Bool)-processTranscript' modproc cmd opts input = do+processTranscript' :: CreateProcess -> Maybe String -> IO (String, Bool)+processTranscript' cp input = do #ifndef mingw32_HOST_OS {- This implementation interleves stdout and stderr in exactly the order  - the process writes them. -} 	(readf, writef) <- System.Posix.IO.createPipe 	readh <- System.Posix.IO.fdToHandle readf 	writeh <- System.Posix.IO.fdToHandle writef-	p@(_, _, _, pid) <- createProcess $ modproc $-		(proc cmd opts)-			{ std_in = if isJust input then CreatePipe else Inherit-			, std_out = UseHandle writeh-			, std_err = UseHandle writeh-			}+	p@(_, _, _, pid) <- createProcess $ cp+		{ std_in = if isJust input then CreatePipe else Inherit+		, std_out = UseHandle writeh+		, std_err = UseHandle writeh+		} 	hClose writeh  	get <- mkreader readh@@ -200,12 +199,11 @@ 	return (transcript, ok) #else {- This implementation for Windows puts stderr after stdout. -}-	p@(_, _, _, pid) <- createProcess $ modproc $-		(proc cmd opts)-			{ std_in = if isJust input then CreatePipe else Inherit-			, std_out = CreatePipe-			, std_err = CreatePipe-			}+	p@(_, _, _, pid) <- createProcess $ cp+		{ std_in = if isJust input then CreatePipe else Inherit+		, std_out = CreatePipe+		, std_err = CreatePipe+		}  	getout <- mkreader (stdoutHandle p) 	geterr <- mkreader (stderrHandle p)
Utility/Url.hs view
@@ -246,17 +246,16 @@ 		(requestHeaders r) 	} -{- Used to download large files, such as the contents of keys.+{- Download a perhaps large file, with auto-resume of incomplete downloads.  -- - Uses wget or curl program for its progress bar. (Wget has a better one,- - so is preferred.) Which program to use is determined at run time; it- - would not be appropriate to test at configure time and build support- - for only one in.+ - Uses wget or curl program for its progress bar and resuming support. + - Which program to use is determined at run time depending on which is+ - in path and which works best in a particular situation.  -} download :: URLString -> FilePath -> UrlOptions -> IO Bool download = download' False -{- No output, even on error. -}+{- No output to stdout. -} downloadQuiet :: URLString -> FilePath -> UrlOptions -> IO Bool downloadQuiet = download' True @@ -265,6 +264,12 @@ 	case parseURIRelaxed url of 		Just u 			| uriScheme u == "file:" -> curl+			-- curl is preferred in quiet mode, because+			-- it displays http errors to stderr, while wget+			-- does not display them in quiet mode+			| quiet -> ifM (inPath "curl") (curl, wget)+			-- wget is preferred mostly because it has a better+			-- progress bar 			| otherwise -> ifM (inPath "wget") (wget , curl) 		_ -> return False   where@@ -275,12 +280,13 @@ 	 - support, or need that option. 	 - 	 - When the wget version is new enough, pass options for-	 - a less cluttered download display.+	 - a less cluttered download display. Using -nv rather than -q+	 - avoids most clutter while still displaying http errors. 	 -} #ifndef __ANDROID__ 	wgetparams = concat-		[ if Build.SysConfig.wgetquietprogress && not quiet-			then [Param "-q", Param "--show-progress"]+		[ if Build.SysConfig.wgetunclutter && not quiet+			then [Param "-nv", Param "--show-progress"] 			else [] 		, [ Param "--clobber", Param "-c", Param "-O"] 		]@@ -296,8 +302,13 @@ 		-- curl does not create destination file 		-- if the url happens to be empty, so pre-create. 		writeFile file ""-		go "curl" $ headerparams ++ quietopt "-s" ++-			[Param "-f", Param "-L", Param "-C", Param "-", Param "-#", Param "-o"]+		go "curl" $ headerparams ++ quietopt "-sS" +++			[ Param "-f"+			, Param "-L"+			, Param "-C", Param "-"+			, Param "-#"+			, Param "-o"+			] 	 	{- Run wget in a temp directory because it has been buggy 	 - and overwritten files in the current directory, even though
doc/git-annex-matching-options.mdwn view
@@ -95,6 +95,11 @@   Matches only files whose content is stored using the specified key-value   backend. +* `--securehash`++  Matches only files whose content is hashed using a cryptographically+  secure function. + * `--inallgroup=groupname`    Matches only files that git-annex believes are present in all repositories
doc/git-annex-merge.mdwn view
@@ -12,10 +12,6 @@ that is done by the sync command, but without pushing or pulling any data. -One way to use this is to put `git annex merge` into a repository's-post-receive hook. Then any syncs to the repository will update its-working copy automatically.- # SEE ALSO  [[git-annex]](1)
doc/git-annex-preferred-content.mdwn view
@@ -96,6 +96,11 @@   Matches only files whose content is stored using the specified key-value   backend. +* `securehash`++  Matches only files whose content is hashed using a cryptographically+  secure function.+ * `inallgroup=groupname`    Matches only files that git-annex believes are present in all repositories
doc/git-annex-status.mdwn view
@@ -23,6 +23,11 @@   Enable JSON output. This is intended to be parsed by programs that use   git-annex. Each line of output is a JSON object. +* `--ignore-submodules=when`++  This option is passed on to git status, see its man page for+  details.+ # SEE ALSO  [[git-annex]](1)
doc/git-annex.mdwn view
@@ -542,6 +542,13 @@      See [[git-annex-pre-commit]](1) for details. +* `post-receive`++  This is meant to be called from git's post-receive hook. `git annex init`+  automatically creates a post-receive hook using this.+  +  See [[git-annex-post-receive]](1) for details.+ * `lookupkey [file ...]`    Looks up key used for file.@@ -816,11 +823,26 @@  * `annex.backends` -  Space-separated list of names of the key-value backends to use.-  The first listed is used to store new files by default.+  Space-separated list of names of the key-value backends to use+  when adding new files to the repository.    This is overridden by annex annex.backend configuration in the   .gitattributes files.++* `annex.securehashesonly`++  Set to true to indicate that the repository should only use+  cryptographically secure hashes+  (SHA2, SHA3) and not insecure hashes (MD5, SHA1) for content.++  When this is set, the contents of files using cryptographically+  insecure hashes will not be allowed to be added to the repository.+  +  Also, git-annex fsck` will complain about any files present in+  the repository that use insecure hashes.+  +  To configure the behavior in new clones of the repository,+  this can be set in [[git-annex-config]].  * `annex.diskreserve` 
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 6.20170214+Version: 6.20170301 Cabal-Version: >= 1.8 License: GPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -295,9 +295,6 @@   Description: Get Network.URI from the network-uri package   Default: True -Flag Cryptonite-  Description: Use the cryptonite library, instead of the older cryptohash- Flag Dbus   Description: Enable dbus support @@ -361,7 +358,9 @@    socks,    byteable,    stm-chans,-   securemem+   securemem,+   crypto-api,+   cryptonite   CC-Options: -Wall   GHC-Options: -Wall -fno-warn-tabs   Extensions: PackageImports@@ -382,12 +381,6 @@   else     Build-Depends: network (< 2.6), network (>= 2.4) -  if flag(Cryptonite)-    Build-Depends: cryptonite-    CPP-Options: -DWITH_CRYPTONITE-  else-    Build-Depends: cryptohash (>= 0.11.0)-   if (os(windows))     Build-Depends: Win32, Win32-extras, unix-compat (>= 0.4.1.3), setenv,       process (>= 1.3.0.0)@@ -397,8 +390,7 @@       Other-Modules: Utility.Touch.Old    if flag(TestSuite)-    Build-Depends: tasty (>= 0.7), tasty-hunit, tasty-quickcheck, tasty-rerun,-     crypto-api+    Build-Depends: tasty (>= 0.7), tasty-hunit, tasty-quickcheck, tasty-rerun     CPP-Options: -DWITH_TESTSUITE    if flag(S3)@@ -461,7 +453,6 @@      wai,      wai-extra,      blaze-builder,-     crypto-api,      clientsession,      template-haskell,      shakespeare (>= 2.0.0)@@ -538,6 +529,7 @@     Annex.Ssh     Annex.TaggedPush     Annex.Transfer+    Annex.UpdateInstead     Annex.UUID     Annex.Url     Annex.VariantFile@@ -743,6 +735,7 @@     Command.NotifyChanges     Command.NumCopies     Command.P2P+    Command.PostReceive     Command.PreCommit     Command.Proxy     Command.ReKey@@ -814,6 +807,7 @@     Git.Command     Git.Command.Batch     Git.Config+    Git.ConfigTypes     Git.Construct     Git.CurrentRepo     Git.DiffTree@@ -839,7 +833,6 @@     Git.Remote.Remove     Git.Repair     Git.Sha-    Git.SharedRepository     Git.Status     Git.Tree     Git.Types@@ -847,6 +840,7 @@     Git.UpdateIndex     Git.Url     Git.Version+    Key     Limit     Limit.Wanted     Logs
stack.yaml view
@@ -4,7 +4,6 @@     production: true     assistant: true     pairing: true-    cryptonite: true     network-uri: true     s3: true     testsuite: true