git-annex 10.20220504 → 10.20220525
raw patch · 36 files changed
+605/−335 lines, 36 files
Files
- Annex/Content.hs +23/−13
- Annex/Content/Presence.hs +2/−1
- Annex/CopyFile.hs +27/−20
- Annex/Import.hs +8/−8
- Annex/Init.hs +17/−2
- Annex/Locations.hs +9/−5
- Annex/Perms.hs +15/−5
- Annex/Verify.hs +32/−11
- Annex/YoutubeDl.hs +1/−1
- CHANGELOG +26/−0
- Command/Fix.hs +1/−1
- Command/Fsck.hs +56/−1
- Command/Info.hs +1/−1
- Command/Lock.hs +2/−2
- Command/TestRemote.hs +1/−1
- Crypto.hs +10/−6
- Git/Config.hs +18/−1
- Remote/Adb.hs +23/−11
- Remote/Borg.hs +15/−5
- Remote/Directory.hs +30/−22
- Remote/External.hs +8/−4
- Remote/Git.hs +2/−4
- Remote/Helper/ExportImport.hs +6/−5
- Remote/Helper/Special.hs +2/−12
- Remote/HttpAlso.hs +6/−6
- Remote/Rsync.hs +6/−2
- Remote/S3.hs +29/−20
- Remote/WebDAV.hs +6/−5
- Test.hs +21/−23
- Test/Framework.hs +110/−75
- Types/Remote.hs +11/−7
- Upgrade/V5/Direct.hs +2/−2
- Utility/DataUnits.hs +36/−12
- Utility/Gpg.hs +40/−38
- Utility/Metered.hs +2/−2
- git-annex.cabal +1/−1
Annex/Content.hs view
@@ -64,6 +64,7 @@ isKeyUnlockedThin, getKeyStatus, getKeyFileStatus,+ cleanObjectDirs, ) where import System.IO.Unsafe (unsafeInterleaveIO)@@ -89,6 +90,7 @@ import Annex.InodeSentinal import Annex.ReplaceFile import Annex.AdjustedBranch (adjustedBranchRefresh)+import Annex.DirHashes import Messages.Progress import Types.Remote (RetrievalSecurityPolicy(..), VerifyConfigA(..)) import Types.NumCopies@@ -169,13 +171,13 @@ posixLocker :: (Maybe FileMode -> LockFile -> Annex (Maybe LockHandle)) -> LockFile -> Annex (Maybe LockHandle) posixLocker takelock lockfile = do mode <- annexFileMode- modifyContent lockfile $+ modifyContentDirWhenExists lockfile $ takelock (Just mode) lockfile #else winLocker :: (LockFile -> IO (Maybe LockHandle)) -> ContentLocker winLocker takelock _ (Just lockfile) = let lck = do- modifyContent lockfile $+ modifyContentDir lockfile $ void $ liftIO $ tryIO $ writeFile (fromRawFilePath lockfile) "" liftIO $ takelock lockfile@@ -262,7 +264,7 @@ cleanuplockfile lockfile = void $ tryNonAsync $ do thawContentDir lockfile liftIO $ removeWhenExistsWith R.removeLink lockfile- liftIO $ cleanObjectDirs lockfile+ cleanObjectDirs lockfile {- Runs an action, passing it the temp file to get, - and if the action succeeds, verifies the file matches@@ -407,7 +409,7 @@ where storeobject dest = ifM (liftIO $ R.doesPathExist dest) ( alreadyhave- , adjustedBranchRefresh af $ modifyContent dest $ do+ , adjustedBranchRefresh af $ modifyContentDir dest $ do liftIO $ moveFile (fromRawFilePath src) (fromRawFilePath dest)@@ -452,7 +454,7 @@ linkToAnnex key src srcic = ifM (checkSecureHashes' key) ( do dest <- calcRepo (gitAnnexLocation key)- modifyContent dest $ linkAnnex To key src srcic dest Nothing+ modifyContentDir dest $ linkAnnex To key src srcic dest Nothing , return LinkAnnexFailed ) @@ -528,7 +530,7 @@ unlinkAnnex :: Key -> Annex () unlinkAnnex key = do obj <- calcRepo (gitAnnexLocation key)- modifyContent obj $ do+ modifyContentDir obj $ do secureErase obj liftIO $ removeWhenExistsWith R.removeLink obj @@ -607,16 +609,24 @@ void $ tryIO $ thawContent file cleaner- liftIO $ cleanObjectDirs file+ cleanObjectDirs file -cleanObjectDirs :: RawFilePath -> IO ()-cleanObjectDirs = go (3 :: Int)+{- Given a filename inside the object directory, tries to remove the object+ - directory, as well as the object hash directories.+ - + - Does nothing if the object directory is not empty, and does not+ - throw an exception if it's unable to remove a directory. -}+cleanObjectDirs :: RawFilePath -> Annex ()+cleanObjectDirs f = do+ HashLevels n <- objectHashLevels <$> Annex.getGitConfig+ liftIO $ go f (succ n) where- go 0 _ = noop- go n file = do+ go _ 0 = noop+ go file n = do let dir = parentDir file- maybe noop (const $ go (n-1) dir)- <=< catchMaybeIO $ removeDirectory (fromRawFilePath dir)+ maybe noop (const $ go dir (n-1))+ <=< catchMaybeIO $ tryWhenExists $+ removeDirectory (fromRawFilePath dir) {- Removes a key's file from .git/annex/objects/ -} removeAnnex :: ContentRemovalLock -> Annex ()
Annex/Content/Presence.hs view
@@ -19,6 +19,7 @@ isUnmodified', isUnmodifiedCheap, withContentLockFile,+ contentLockFile, ) where import Annex.Content.Presence.LowLevel@@ -112,7 +113,7 @@ - remove the lock file to clean up after ourselves. -} checklock (Just lockfile) contentfile = ifM (liftIO $ doesFileExist (fromRawFilePath contentfile))- ( modifyContent lockfile $ liftIO $+ ( modifyContentDir lockfile $ liftIO $ lockShared lockfile >>= \case Nothing -> return is_locked Just lockhandle -> do
Annex/CopyFile.hs view
@@ -1,6 +1,6 @@ {- Copying files. -- - Copyright 2011-2021 Joey Hess <id@joeyh.name>+ - Copyright 2011-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -79,40 +79,47 @@ - (eg when isStableKey is false), and doing this avoids getting a - corrupted file in such cases. -}-fileCopier :: CopyCoWTried -> FilePath -> FilePath -> MeterUpdate -> Maybe IncrementalVerifier -> Annex CopyMethod+fileCopier :: CopyCoWTried -> FilePath -> FilePath -> MeterUpdate -> Maybe IncrementalVerifier -> IO CopyMethod #ifdef mingw32_HOST_OS fileCopier _ src dest meterupdate iv = docopy #else fileCopier copycowtried src dest meterupdate iv =- ifM (liftIO $ tryCopyCoW copycowtried src dest meterupdate)+ ifM (tryCopyCoW copycowtried src dest meterupdate) ( do- liftIO $ maybe noop unableIncrementalVerifier iv+ maybe noop unableIncrementalVerifier iv return CopiedCoW , docopy ) #endif where- dest' = toRawFilePath dest- docopy = do -- The file might have had the write bit removed, -- so make sure we can write to it.- void $ liftIO $ tryIO $ allowWrite dest'-- liftIO $ withBinaryFile dest ReadWriteMode $ \hdest ->- withBinaryFile src ReadMode $ \hsrc -> do- sofar <- compareexisting hdest hsrc zeroBytesProcessed- docopy' hdest hsrc sofar+ void $ tryIO $ allowWrite dest' + withBinaryFile src ReadMode $ \hsrc ->+ fileContentCopier hsrc dest meterupdate iv+ -- Copy src mode and mtime.- mode <- liftIO $ fileMode <$> getFileStatus src- mtime <- liftIO $ utcTimeToPOSIXSeconds <$> getModificationTime src- liftIO $ setFileMode dest mode- liftIO $ touch dest' mtime False+ mode <- fileMode <$> getFileStatus src+ mtime <- utcTimeToPOSIXSeconds <$> getModificationTime src+ setFileMode dest mode+ touch dest' mtime False return Copied - docopy' hdest hsrc sofar = do+ dest' = toRawFilePath dest++{- Copies content from a handle to a destination file. Does not+ - use copy-on-write, and does not copy file mode and mtime.+ -}+fileContentCopier :: Handle -> FilePath -> MeterUpdate -> Maybe IncrementalVerifier -> IO ()+fileContentCopier hsrc dest meterupdate iv =+ withBinaryFile dest ReadWriteMode $ \hdest -> do+ sofar <- compareexisting hdest zeroBytesProcessed+ docopy hdest sofar+ where+ docopy hdest sofar = do s <- S.hGet hsrc defaultChunkSize if s == S.empty then return ()@@ -121,12 +128,12 @@ S.hPut hdest s maybe noop (flip updateIncrementalVerifier s) iv meterupdate sofar'- docopy' hdest hsrc sofar'+ docopy hdest sofar' -- Leaves hdest and hsrc seeked to wherever the two diverge, -- so typically hdest will be seeked to end, and hsrc to the same -- position.- compareexisting hdest hsrc sofar = do+ compareexisting hdest sofar = do s <- S.hGet hdest defaultChunkSize if s == S.empty then return sofar@@ -137,7 +144,7 @@ maybe noop (flip updateIncrementalVerifier s) iv let sofar' = addBytesProcessed sofar (S.length s) meterupdate sofar'- compareexisting hdest hsrc sofar'+ compareexisting hdest sofar' else do seekbefore hdest s seekbefore hsrc s'
Annex/Import.hs view
@@ -597,14 +597,14 @@ getcontent k = do let af = AssociatedFile (Just f) let downloader p' tmpfile = do- k' <- Remote.retrieveExportWithContentIdentifier+ _ <- Remote.retrieveExportWithContentIdentifier ia loc cid (fromRawFilePath tmpfile)- (pure k)+ (Left k) (combineMeterUpdate p' p)- ok <- moveAnnex k' af tmpfile+ ok <- moveAnnex k af tmpfile when ok $ logStatus k InfoPresent- return (Just (k', ok))+ return (Just (k, ok)) checkDiskSpaceToGet k Nothing $ notifyTransfer Download af $ download' (Remote.uuid remote) k af Nothing stdRetry $ \p' ->@@ -615,9 +615,9 @@ -- need to retrieve this file. doimportsmall cidmap db loc cid sz p = do let downloader tmpfile = do- k <- Remote.retrieveExportWithContentIdentifier+ (k, _) <- Remote.retrieveExportWithContentIdentifier ia loc cid (fromRawFilePath tmpfile)- (mkkey tmpfile)+ (Right (mkkey tmpfile)) p case keyGitSha k of Just sha -> do@@ -638,9 +638,9 @@ dodownload cidmap db (loc, (cid, sz)) f matcher = do let af = AssociatedFile (Just f) let downloader tmpfile p = do- k <- Remote.retrieveExportWithContentIdentifier+ (k, _) <- Remote.retrieveExportWithContentIdentifier ia loc cid (fromRawFilePath tmpfile)- (mkkey tmpfile)+ (Right (mkkey tmpfile)) p case keyGitSha k of Nothing -> do
Annex/Init.hs view
@@ -68,7 +68,7 @@ #endif checkInitializeAllowed :: Annex a -> Annex a-checkInitializeAllowed a = noAnnexFileContent' >>= \case+checkInitializeAllowed a = guardSafeToUseRepo $ noAnnexFileContent' >>= \case Nothing -> a Just noannexmsg -> do warning "Initialization prevented by .noannex file (remove the file to override)"@@ -216,7 +216,7 @@ objectDirNotPresent = do d <- fromRawFilePath <$> fromRepo gitAnnexObjectDir exists <- liftIO $ doesDirectoryExist d- when exists $+ when exists $ guardSafeToUseRepo $ giveup $ unwords $ [ "This repository is not initialized for use" , "by git-annex, but " ++ d ++ " exists,"@@ -227,6 +227,21 @@ , "to initialize with a new uuid." ] return (not exists)++guardSafeToUseRepo :: Annex a -> Annex a+guardSafeToUseRepo a = do+ repopath <- fromRepo Git.repoPath+ ifM (inRepo Git.Config.checkRepoConfigInaccessible)+ ( giveup $ unlines $+ [ "Git refuses to operate in this repository,"+ , "probably because it is owned by someone else."+ , ""+ -- This mirrors git's wording.+ , "To add an exception for this directory, call:"+ , "\tgit config --global --add safe.directory " ++ fromRawFilePath repopath+ ]+ , a+ ) {- Initialize if it can do so automatically. Avoids failing if it cannot. -
Annex/Locations.hs view
@@ -16,6 +16,7 @@ objectDir, objectDir', gitAnnexLocation,+ gitAnnexLocation', gitAnnexLocationDepth, gitAnnexLink, gitAnnexLinkCanonical,@@ -172,14 +173,17 @@ - be stored. -} gitAnnexLocation :: Key -> Git.Repo -> GitConfig -> IO RawFilePath-gitAnnexLocation key r config = gitAnnexLocation' key r config+gitAnnexLocation = gitAnnexLocation' R.doesPathExist++gitAnnexLocation' :: (RawFilePath -> IO Bool) -> Key -> Git.Repo -> GitConfig -> IO RawFilePath+gitAnnexLocation' checker key r config = gitAnnexLocation'' key r config (annexCrippledFileSystem config) (coreSymlinks config)- R.doesPathExist+ checker (Git.localGitDir r) -gitAnnexLocation' :: Key -> Git.Repo -> GitConfig -> Bool -> Bool -> (RawFilePath -> IO Bool) -> RawFilePath -> IO RawFilePath-gitAnnexLocation' key r config crippled symlinkssupported checker gitdir+gitAnnexLocation'' :: Key -> Git.Repo -> GitConfig -> Bool -> Bool -> (RawFilePath -> IO Bool) -> RawFilePath -> IO RawFilePath+gitAnnexLocation'' key r config crippled symlinkssupported checker gitdir {- Bare repositories default to hashDirLower for new - content, as it's more portable. But check all locations. -} | Git.repoIsLocalBare r = checkall annexLocationsBare@@ -207,7 +211,7 @@ currdir <- R.getCurrentDirectory let absfile = absNormPathUnix currdir file let gitdir = getgitdir currdir- loc <- gitAnnexLocation' key r config False False (\_ -> return True) gitdir+ loc <- gitAnnexLocation'' key r config False False (\_ -> return True) gitdir toInternalGitPath <$> relPathDirToFile (parentDir absfile) loc where getgitdir currdir
Annex/Perms.hs view
@@ -26,7 +26,8 @@ createContentDir, freezeContentDir, thawContentDir,- modifyContent,+ modifyContentDir,+ modifyContentDirWhenExists, withShared, hasFreezeHook, hasThawHook,@@ -290,11 +291,20 @@ dir = parentDir dest {- Creates the content directory for a file if it doesn't already exist,- - or thaws it if it does, then runs an action to modify the file, and- - finally, freezes the content directory. -}-modifyContent :: RawFilePath -> Annex a -> Annex a-modifyContent f a = do+ - or thaws it if it does, then runs an action to modify a file in the+ - directory, and finally, freezes the content directory. -}+modifyContentDir :: RawFilePath -> Annex a -> Annex a+modifyContentDir f a = do createContentDir f -- also thaws it+ v <- tryNonAsync a+ freezeContentDir f+ either throwM return v++{- Like modifyContentDir, but avoids creating the content directory if it+ - does not already exist. In that case, the action will probably fail. -}+modifyContentDirWhenExists :: RawFilePath -> Annex a -> Annex a+modifyContentDirWhenExists f a = do+ thawContentDir f v <- tryNonAsync a freezeContentDir f either throwM return v
Annex/Verify.hs view
@@ -1,6 +1,6 @@ {- verification -- - Copyright 2010-2021 Joey Hess <id@joeyh.name>+ - Copyright 2010-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -17,6 +17,7 @@ isVerifiable, startVerifyKeyContentIncrementally, finishVerifyKeyContentIncrementally,+ verifyKeyContentIncrementally, IncrementalVerifier(..), tailVerify, ) where@@ -34,6 +35,7 @@ import Types.Key import Control.Concurrent.STM+import Control.Concurrent.Async import qualified Data.ByteString as S #if WITH_INOTIFY import qualified System.INotify as INotify@@ -79,15 +81,19 @@ ( verify , return True )- (_, MustVerify) -> verify (_, IncompleteVerify _) -> ifM (shouldVerify v) ( verify , return True )+ (_, MustVerify) -> verify+ (_, MustFinishIncompleteVerify _) -> verify where verify = enteringStage VerifyStage $ case verification of- IncompleteVerify iv -> resumeVerifyKeyContent k f iv+ IncompleteVerify iv -> + resumeVerifyKeyContent k f iv+ MustFinishIncompleteVerify iv -> + resumeVerifyKeyContent k f iv _ -> verifyKeyContent k f verifyKeyContent :: Key -> RawFilePath -> Annex Bool@@ -182,12 +188,17 @@ -- Incremental verification was not able to be done. Nothing -> return (True, UnVerified) --- | Reads the file as it grows, and feeds it to the incremental verifier.+verifyKeyContentIncrementally :: VerifyConfig -> Key -> (Maybe IncrementalVerifier -> Annex ()) -> Annex Verification+verifyKeyContentIncrementally verifyconfig k a = do+ miv <- startVerifyKeyContentIncrementally verifyconfig k+ a miv+ snd <$> finishVerifyKeyContentIncrementally miv++-- | Runs a writer action that retrieves to a file. In another thread,+-- reads the file as it grows, and feeds it to the incremental verifier. -- --- The TMVar must start out empty, and be filled once whatever is--- writing to the file finishes. Once the writer finishes, this returns--- quickly. It may not feed the entire content of the file to the--- incremental verifier.+-- Once the writer finishes, this returns quickly. It may not feed+-- the entire content of the file to the incremental verifier. -- -- The file does not need to exist yet when this is called. It will wait -- for the file to appear before opening it and starting verification.@@ -219,9 +230,19 @@ -- and if the disk is slow, the reader may never catch up to the writer, -- and the disk cache may never speed up reads. So this should only be -- used when there's not a better way to incrementally verify.-tailVerify :: IncrementalVerifier -> RawFilePath -> TMVar () -> IO ()+tailVerify :: Maybe IncrementalVerifier -> RawFilePath -> Annex a -> Annex a+tailVerify (Just iv) f writer = do+ finished <- liftIO newEmptyTMVarIO+ t <- liftIO $ async $ tailVerify' iv f finished+ let finishtail = do+ liftIO $ atomically $ putTMVar finished ()+ liftIO (wait t)+ writer `finally` finishtail+tailVerify Nothing _ writer = writer++tailVerify' :: IncrementalVerifier -> RawFilePath -> TMVar () -> IO () #if WITH_INOTIFY-tailVerify iv f finished = +tailVerify' iv f finished = tryNonAsync go >>= \case Right r -> return r Left _ -> unableIncrementalVerifier iv@@ -308,5 +329,5 @@ chunk = 65536 #else-tailVerify iv _ _ = unableIncrementalVerifier iv+tailVerify' iv _ _ = unableIncrementalVerifier iv #endif
Annex/YoutubeDl.hs view
@@ -295,4 +295,4 @@ parsebytes = readSize units . takeWhile (not . isSpace) - units = memoryUnits ++ storageUnits+ units = committeeUnits ++ storageUnits
CHANGELOG view
@@ -1,3 +1,29 @@+git-annex (10.20220525) upstream; urgency=medium++ * Special remotes with importtree=yes or exporttree=yes are once again+ treated as untrusted, since files stored in them can be deleted or+ modified at any time.+ (Fixes a reversion in 8.20201129)+ * Added support for "megabit" and related bandwidth units+ in annex.stalldetection and everywhere else that git-annex parses+ data units. Note that the short form is "Mbit" not "Mb" because+ that differs from "MB" only in case, and git-annex parses units+ case-insensitively.+ * Special remotes using exporttree=yes and/or importtree=yes now+ checksum content while it is being retrieved, instead of in a separate+ pass at the end.+ * fsck: Fix situations where the annex object file is stored in a+ directory structure other than where annex symlinks point to.+ * Deal with git's recent changes to fix CVE-2022-24765, which prevent+ using git in a repository owned by someone else.+ * Improve an error message displayed in that situation.+ * Prevent git-annex init incorrectly reinitializing the repository in+ that situation.+ * test: When limiting tests to run with -p, work around tasty limitation+ by automatically including dependent tests.++ -- Joey Hess <id@joeyh.name> Wed, 25 May 2022 13:44:46 -0400+ git-annex (10.20220504) upstream; urgency=medium * Ignore annex.numcopies set to 0 in gitattributes or git config,
Command/Fix.hs view
@@ -78,7 +78,7 @@ error "unable to break hard link" thawContent tmp' Database.Keys.storeInodeCaches key [tmp']- modifyContent obj $ freezeContent obj+ modifyContentDir obj $ freezeContent obj next $ return True makeHardLink :: RawFilePath -> Key -> CommandPerform
Command/Fsck.hs view
@@ -1,6 +1,6 @@ {- git-annex command -- - Copyright 2010-2021 Joey Hess <id@joeyh.name>+ - Copyright 2010-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -16,9 +16,11 @@ import qualified Types.Backend import qualified Backend import Annex.Content+import Annex.Content.Presence import Annex.Content.Presence.LowLevel import Annex.Perms import Annex.Link+import Annex.Version import Logs.Location import Logs.Trust import Logs.Activity@@ -134,6 +136,7 @@ check -- order matters [ fixLink key file+ , fixObjectLocation key , verifyLocationLog key keystatus ai , verifyRequiredContent key ai , verifyAssociatedFiles key keystatus file@@ -242,6 +245,58 @@ liftIO $ R.removeLink file addAnnexLink want file | otherwise = noop++{- A repository that supports symlinks and is not bare may have in the past+ - been bare, or not supported symlinks. If so, the object may be located+ - in a directory other than the one where annex symlinks point to. Moves+ - the object in that case.+ -+ - Also if a repository has been converted to bare, or moved to a crippled+ - filesystem not supporting symlinks, the object file will be moved+ - to the other location.+ -}+fixObjectLocation :: Key -> Annex Bool+fixObjectLocation key = do+#ifdef mingw32_HOST_OS+ -- Windows does not allow locked files to be renamed, but annex+ -- links are also not used on Windows.+ return True+#else+ loc <- calcRepo (gitAnnexLocation key)+ idealloc <- calcRepo (gitAnnexLocation' (const (pure True)) key)+ if loc == idealloc+ then return True+ else ifM (liftIO $ R.doesPathExist loc)+ ( moveobjdir loc idealloc+ `catchNonAsync` \_e -> return True+ , return True+ )+ where+ moveobjdir src dest = do+ let srcdir = parentDir src+ let destdir = parentDir dest+ showNote "normalizing object location"+ -- When the content file is moved, it will+ -- appear to other processes as if it has been removed.+ -- That should never happen to a process that has used+ -- lockContentShared, so avoid it by locking the content+ -- for removal, although it's not really being removed.+ lockContentForRemoval key (return True) $ \_lck -> do+ -- Thaw the content directory to allow renaming it.+ thawContentDir src+ createAnnexDirectory (parentDir destdir)+ liftIO $ renameDirectory+ (fromRawFilePath srcdir)+ (fromRawFilePath destdir)+ -- Since the directory was moved, lockContentForRemoval+ -- will not be able to remove the lock file it+ -- made. So, remove the lock file here.+ mlockfile <- contentLockFile key =<< getVersion+ liftIO $ maybe noop (removeWhenExistsWith R.removeLink) mlockfile+ freezeContentDir dest+ cleanObjectDirs src+ return True+#endif {- Checks that the location log reflects the current status of the key, - in this repository only. -}
Command/Info.hs view
@@ -422,7 +422,7 @@ -- Two bloom filters are used at the same time when running -- git-annex unused, so double the size of one. sizer <- mkSizer- size <- sizer memoryUnits False . (* 2) . fromIntegral . fst <$>+ size <- sizer committeeUnits False . (* 2) . fromIntegral . fst <$> lift bloomBitsHashes return $ size ++ note
Command/Lock.hs view
@@ -77,13 +77,13 @@ breakhardlink obj = whenM (catchBoolIO $ (> 1) . linkCount <$> liftIO (R.getFileStatus obj)) $ do mfc <- withTSDelta (liftIO . genInodeCache file) unlessM (sameInodeCache obj (maybeToList mfc)) $ do- modifyContent obj $ replaceGitAnnexDirFile (fromRawFilePath obj) $ \tmp -> do+ modifyContentDir obj $ replaceGitAnnexDirFile (fromRawFilePath obj) $ \tmp -> do unlessM (checkedCopyFile key obj (toRawFilePath tmp) Nothing) $ giveup "unable to lock file" Database.Keys.storeInodeCaches key [obj] -- Try to repopulate obj from an unmodified associated file.- repopulate obj = modifyContent obj $ do+ repopulate obj = modifyContentDir obj $ do g <- Annex.gitRepo fs <- map (`fromTopFilePath` g) <$> Database.Keys.getAssociatedFiles key
Command/TestRemote.hs view
@@ -354,7 +354,7 @@ liftIO $ hClose h tryNonAsync (Remote.retrieveExport ea k testexportlocation tmp nullMeterUpdate) >>= \case Left _ -> return False- Right () -> verifyKeyContentPostRetrieval RetrievalAllKeysSecure AlwaysVerify UnVerified k (toRawFilePath tmp)+ Right v -> verifyKeyContentPostRetrieval RetrievalAllKeysSecure AlwaysVerify v k (toRawFilePath tmp) checkpresentexport ea k = Remote.checkPresentExport ea k testexportlocation removeexport ea k = Remote.removeExport ea k testexportlocation removeexportdirectory ea = case Remote.removeExportDirectory ea of
Crypto.hs view
@@ -3,7 +3,7 @@ - Currently using gpg; could later be modified to support different - crypto backends if neccessary. -- - Copyright 2011-2020 Joey Hess <id@joeyh.name>+ - Copyright 2011-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -22,7 +22,8 @@ genSharedCipher, genSharedPubKeyCipher, updateCipherKeyIds,- decryptCipher, + decryptCipher,+ decryptCipher', encryptKey, isEncKey, feedFile,@@ -147,10 +148,13 @@ {- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -} decryptCipher :: LensGpgEncParams c => Gpg.GpgCmd -> c -> StorableCipher -> IO Cipher-decryptCipher _ _ (SharedCipher t) = return $ Cipher t-decryptCipher _ _ (SharedPubKeyCipher t _) = return $ MacOnlyCipher t-decryptCipher cmd c (EncryptedCipher t variant _) =- mkCipher <$> Gpg.pipeStrict cmd params t+decryptCipher cmd c cip = decryptCipher' cmd Nothing c cip++decryptCipher' :: LensGpgEncParams c => Gpg.GpgCmd -> Maybe [(String, String)] -> c -> StorableCipher -> IO Cipher+decryptCipher' _ _ _ (SharedCipher t) = return $ Cipher t+decryptCipher' _ _ _ (SharedPubKeyCipher t _) = return $ MacOnlyCipher t+decryptCipher' cmd environ c (EncryptedCipher t variant _) =+ mkCipher <$> Gpg.pipeStrict' cmd params environ t where mkCipher = case variant of Hybrid -> Cipher
Git/Config.hs view
@@ -1,6 +1,6 @@ {- git repository configuration handling -- - Copyright 2010-2020 Joey Hess <id@joeyh.name>+ - Copyright 2010-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -22,6 +22,7 @@ import qualified Git.Command import qualified Git.Construct import Utility.UserInfo+import Utility.Process.Transcript {- Returns a single git config setting, or a fallback value if not set. -} get :: ConfigKey -> ConfigValue -> Repo -> ConfigValue@@ -273,3 +274,19 @@ ) where ps = [Param "config", Param "--unset-all", Param (decodeBS k)]++{- git "fixed" CVE-2022-24765 by preventing git-config from+ - listing per-repo configs when the repo is not owned by+ - the current user. Detect if this fix is in effect for the+ - repo.+ -}+checkRepoConfigInaccessible :: Repo -> IO Bool+checkRepoConfigInaccessible r = do+ -- Cannot use gitCommandLine here because specifying --git-dir+ -- will bypass the git security check.+ let p = (proc "git" ["config", "--local", "--list"])+ { cwd = Just (fromRawFilePath (repoPath r))+ , env = gitEnv r+ }+ (_out, ok) <- processTranscript' p Nothing+ return (not ok)
Remote/Adb.hs view
@@ -1,6 +1,6 @@ {- Remote on Android device accessed using adb. -- - Copyright 2018-2020 Joey Hess <id@joeyh.name>+ - Copyright 2018-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -22,6 +22,7 @@ import Utility.Metered import Types.ProposedAccepted import Annex.SpecialRemote.Config+import Annex.Verify import qualified Data.Map as M import qualified System.FilePath.Posix as Posix@@ -255,8 +256,11 @@ where dest = androidExportLocation adir loc -retrieveExportM :: AndroidSerial -> AndroidPath -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex ()-retrieveExportM serial adir _k loc dest _p = retrieve' serial src dest+retrieveExportM :: AndroidSerial -> AndroidPath -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification+retrieveExportM serial adir k loc dest _p = + verifyKeyContentIncrementally AlwaysVerify k $ \iv ->+ tailVerify iv (toRawFilePath dest) $+ retrieve' serial src dest where src = androidExportLocation adir loc @@ -334,15 +338,23 @@ -- connection is resonably fast, it's probably as good as -- git's handling of similar situations with files being modified while -- it's updating the working tree for a merge.-retrieveExportWithContentIdentifierM :: AndroidSerial -> AndroidPath -> ExportLocation -> ContentIdentifier -> FilePath -> Annex Key -> MeterUpdate -> Annex Key-retrieveExportWithContentIdentifierM serial adir loc cid dest mkkey _p = do- retrieve' serial src dest- k <- mkkey- currcid <- getExportContentIdentifier serial adir loc- if currcid == Right (Just cid)- then return k- else giveup "the file on the android device has changed"+retrieveExportWithContentIdentifierM :: AndroidSerial -> AndroidPath -> ExportLocation -> ContentIdentifier -> FilePath -> Either Key (Annex Key) -> MeterUpdate -> Annex (Key, Verification)+retrieveExportWithContentIdentifierM serial adir loc cid dest gk _p = do+ case gk of+ Right mkkey -> do+ go+ k <- mkkey+ return (k, UnVerified)+ Left k -> do+ v <- verifyKeyContentIncrementally DefaultVerify k+ (\iv -> tailVerify iv (toRawFilePath dest) go)+ return (k, v) where+ go = do+ retrieve' serial src dest+ currcid <- getExportContentIdentifier serial adir loc+ when (currcid /= Right (Just cid)) $+ giveup "the file on the android device has changed" src = androidExportLocation adir loc storeExportWithContentIdentifierM :: AndroidSerial -> AndroidPath -> FilePath -> Key -> ExportLocation -> [ContentIdentifier] -> MeterUpdate -> Annex ContentIdentifier
Remote/Borg.hs view
@@ -29,6 +29,7 @@ import Logs.Export import qualified Remote.Helper.ThirdPartyPopulated as ThirdPartyPopulated import Utility.Env+import Annex.Verify import Data.Either import Text.Read@@ -370,10 +371,20 @@ , giveup $ "Unable to access borg repository " ++ locBorgRepo borgrepo ) -retrieveExportWithContentIdentifierM :: BorgRepo -> ImportLocation -> ContentIdentifier -> FilePath -> Annex Key -> MeterUpdate -> Annex Key-retrieveExportWithContentIdentifierM borgrepo loc _ dest mkk _ = do+retrieveExportWithContentIdentifierM :: BorgRepo -> ImportLocation -> ContentIdentifier -> FilePath -> Either Key (Annex Key) -> MeterUpdate -> Annex (Key, Verification)+retrieveExportWithContentIdentifierM borgrepo loc _ dest gk _ = do showOutput- prompt $ withOtherTmp $ \othertmp -> liftIO $ do+ case gk of+ Right mkkey -> do+ go+ k <- mkkey+ return (k, UnVerified)+ Left k -> do+ v <- verifyKeyContentIncrementally DefaultVerify k + (\iv -> tailVerify iv (toRawFilePath dest) go)+ return (k, v)+ where+ go = prompt $ withOtherTmp $ \othertmp -> liftIO $ do -- borgrepo could be relative, and borg has to be run -- in the temp directory to get it to write there absborgrepo <- absBorgRepo borgrepo@@ -398,6 +409,5 @@ -- combine with </> moveFile (fromRawFilePath othertmp </> fromRawFilePath archivefile) dest removeDirectoryRecursive (fromRawFilePath othertmp)- mkk- where+ (archivename, archivefile) = extractImportLocation loc
Remote/Directory.hs view
@@ -36,6 +36,7 @@ import Annex.Content import Annex.Perms import Annex.UUID+import Annex.Verify import Backend import Types.KeySource import Types.ProposedAccepted@@ -197,9 +198,9 @@ (fromRawFilePath destdir) in byteStorer go k c m NoChunks ->- let go _k src p = do+ let go _k src p = liftIO $ do void $ fileCopier cow src tmpf p Nothing- liftIO $ finalizeStoreGeneric d tmpdir destdir+ finalizeStoreGeneric d tmpdir destdir in fileStorer go k c m _ -> let go _k b p = liftIO $ do@@ -241,7 +242,7 @@ retrieveKeyFileM d (LegacyChunks _) _ = Legacy.retrieve locations d retrieveKeyFileM d NoChunks cow = fileRetriever' $ \dest k p iv -> do src <- liftIO $ fromRawFilePath <$> getLocation d k- void $ fileCopier cow src (fromRawFilePath dest) p iv+ void $ liftIO $ fileCopier cow src (fromRawFilePath dest) p iv retrieveKeyFileM d _ _ = byteRetriever $ \k sink -> sink =<< liftIO (L.readFile . fromRawFilePath =<< getLocation d k) @@ -314,10 +315,12 @@ viaTmp go (fromRawFilePath dest) () where dest = exportPath d loc- go tmp () = void $ fileCopier cow src tmp p Nothing+ go tmp () = void $ liftIO $ fileCopier cow src tmp p Nothing -retrieveExportM :: RawFilePath -> CopyCoWTried -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex ()-retrieveExportM d cow _k loc dest p = void $ fileCopier cow src dest p Nothing+retrieveExportM :: RawFilePath -> CopyCoWTried -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification+retrieveExportM d cow k loc dest p = + verifyKeyContentIncrementally AlwaysVerify k $ \iv -> + void $ liftIO $ fileCopier cow src dest p iv where src = fromRawFilePath $ exportPath d loc @@ -410,25 +413,31 @@ , inodeCache = Nothing } -retrieveExportWithContentIdentifierM :: IgnoreInodes -> RawFilePath -> CopyCoWTried -> ExportLocation -> ContentIdentifier -> FilePath -> Annex Key -> MeterUpdate -> Annex Key-retrieveExportWithContentIdentifierM ii dir cow loc cid dest mkkey p = - precheck docopy+retrieveExportWithContentIdentifierM :: IgnoreInodes -> RawFilePath -> CopyCoWTried -> ExportLocation -> ContentIdentifier -> FilePath -> Either Key (Annex Key) -> MeterUpdate -> Annex (Key, Verification)+retrieveExportWithContentIdentifierM ii dir cow loc cid dest gk p =+ case gk of+ Right mkkey -> do+ go Nothing+ k <- mkkey+ return (k, UnVerified)+ Left k -> do+ v <- verifyKeyContentIncrementally DefaultVerify k go+ return (k, v) where f = exportPath dir loc f' = fromRawFilePath f+ + go iv = precheck (docopy iv) - docopy = ifM (liftIO $ tryCopyCoW cow f' dest p)- ( do- k <- mkkey- postcheckcow (return k)- , docopynoncow+ docopy iv = ifM (liftIO $ tryCopyCoW cow f' dest p)+ ( postcheckcow (liftIO $ maybe noop unableIncrementalVerifier iv)+ , docopynoncow iv ) - docopynoncow = do+ docopynoncow iv = do #ifndef mingw32_HOST_OS let open = do- -- Need a duplicate fd for the post check, since- -- hGetContentsMetered closes its handle.+ -- Need a duplicate fd for the post check. fd <- openFd f' ReadOnly Nothing defaultFileFlags dupfd <- dup fd h <- fdToHandle fd@@ -442,12 +451,11 @@ let close = hClose bracketIO open close $ \h -> do #endif- liftIO $ hGetContentsMetered h p >>= L.writeFile dest- k <- mkkey+ liftIO $ fileContentCopier h dest p iv #ifndef mingw32_HOST_OS- postchecknoncow dupfd (return k)+ postchecknoncow dupfd (return ()) #else- postchecknoncow (return k)+ postchecknoncow (return ()) #endif -- Check before copy, to avoid expensive copy of wrong file@@ -497,7 +505,7 @@ liftIO $ createDirectoryUnder dir (toRawFilePath destdir) withTmpFileIn destdir template $ \tmpf tmph -> do liftIO $ hClose tmph- void $ fileCopier cow src tmpf p Nothing+ void $ liftIO $ fileCopier cow src tmpf p Nothing let tmpf' = toRawFilePath tmpf resetAnnexFilePerm tmpf' liftIO (getFileStatus tmpf) >>= liftIO . mkContentIdentifier ii tmpf' >>= \case
Remote/External.hs view
@@ -1,6 +1,6 @@ {- External special remote interface. -- - Copyright 2013-2020 Joey Hess <id@joeyh.name>+ - Copyright 2013-2022 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -37,6 +37,7 @@ import Annex.Content import Annex.Url import Annex.UUID+import Annex.Verify import Creds import Control.Concurrent.STM@@ -291,8 +292,11 @@ _ -> Nothing req sk = TRANSFEREXPORT Upload sk f -retrieveExportM :: External -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex ()-retrieveExportM external k loc d p = either giveup return =<< go+retrieveExportM :: External -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification+retrieveExportM external k loc dest p = do+ verifyKeyContentIncrementally AlwaysVerify k $ \iv ->+ tailVerify iv (toRawFilePath dest) $+ either giveup return =<< go where go = handleRequestExport external loc req k (Just p) $ \resp -> case resp of TRANSFER_SUCCESS Download k'@@ -302,7 +306,7 @@ UNSUPPORTED_REQUEST -> result $ Left "TRANSFEREXPORT not implemented by external special remote" _ -> Nothing- req sk = TRANSFEREXPORT Download sk d+ req sk = TRANSFEREXPORT Download sk dest checkPresentExportM :: External -> Key -> ExportLocation -> Annex Bool checkPresentExportM external k loc = either giveup id <$> go
Remote/Git.hs view
@@ -491,14 +491,12 @@ copyFromRemote'' :: Git.Repo -> Remote -> State -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> VerifyConfig -> Annex Verification copyFromRemote'' repo r st@(State connpool _ _ _ _) key file dest meterupdate vc- | Git.repoIsHttp repo = do- iv <- startVerifyKeyContentIncrementally vc key+ | Git.repoIsHttp repo = verifyKeyContentIncrementally vc key $ \iv -> do gc <- Annex.getGitConfig ok <- Url.withUrlOptionsPromptingCreds $ Annex.Content.downloadUrl False key meterupdate iv (keyUrls gc repo r key) dest unless ok $ giveup "failed to download content"- snd <$> finishVerifyKeyContentIncrementally iv | not $ Git.repoIsUrl repo = guardUsable repo (giveup "cannot access remote") $ do u <- getUUID hardlink <- wantHardLink@@ -717,7 +715,7 @@ where copier src dest k p check verifyconfig = do iv <- startVerifyKeyContentIncrementally verifyconfig k- fileCopier copycowtried src dest p iv >>= \case+ liftIO (fileCopier copycowtried src dest p iv) >>= \case Copied -> ifM check ( finishVerifyKeyContentIncrementally iv , do
Remote/Helper/ExportImport.hs view
@@ -216,7 +216,7 @@ , untrustworthy = if versioned || thirdPartyPopulated (remotetype r) then untrustworthy r- else False+ else True -- git-annex testremote cannot be used to test -- import/export since it stores keys. , mkUnavailable = return Nothing@@ -349,8 +349,10 @@ retrieveKeyFileFromExport dbv k _af dest p = ifM (isVerifiable k) ( do l <- getfirstexportloc dbv k- retrieveExport (exportActions r) k l dest p- return MustVerify+ retrieveExport (exportActions r) k l dest p >>= return . \case+ UnVerified -> MustVerify+ IncompleteVerify iv -> MustFinishIncompleteVerify iv+ v -> v , giveup $ "exported content cannot be verified due to using the " ++ decodeBS (formatKeyVariety (fromKey keyVariety k)) ++ " backend" ) @@ -358,8 +360,7 @@ getkeycids ciddbv k >>= \case (cid:_) -> do l <- getfirstexportloc dbv k- void $ retrieveExportWithContentIdentifier (importActions r) l cid dest (pure k) p- return UnVerified+ snd <$> retrieveExportWithContentIdentifier (importActions r) l cid dest (Left k) p -- In case a content identifier is somehow missing, -- try this instead. [] -> if isexport
Remote/Helper/Special.hs view
@@ -56,8 +56,6 @@ import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import qualified Data.Map as M-import Control.Concurrent.STM-import Control.Concurrent.Async {- Special remotes don't have a configured url, so Git.Repo does not - automatically generate remotes for them. This looks for a different@@ -117,17 +115,9 @@ -- the action writes to the file, but may not be updated with the entire -- content of the file. fileRetriever :: (RawFilePath -> Key -> MeterUpdate -> Annex ()) -> Retriever-fileRetriever a = fileRetriever' $ \f k m miv -> do+fileRetriever a = fileRetriever' $ \f k m miv -> let retrieve = a f k m- case miv of- Nothing -> retrieve- Just iv -> do- finished <- liftIO newEmptyTMVarIO- t <- liftIO $ async $ tailVerify iv f finished- let finishtail = do- liftIO $ atomically $ putTMVar finished ()- liftIO (wait t)- retrieve `finally` finishtail+ in tailVerify miv f retrieve {- A Retriever that writes the content of a Key to a provided file. - The action is responsible for updating the progress meter and the
Remote/HttpAlso.hs view
@@ -116,13 +116,13 @@ downloadKey :: Maybe URLString -> LearnedLayout -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> VerifyConfig -> Annex Verification downloadKey baseurl ll key _af dest p vc = do- iv <- startVerifyKeyContentIncrementally vc key- downloadAction dest p iv key (keyUrlAction baseurl ll key)- snd <$> finishVerifyKeyContentIncrementally iv+ verifyKeyContentIncrementally vc key $ \iv ->+ downloadAction dest p iv key (keyUrlAction baseurl ll key) -retriveExportHttpAlso :: Maybe URLString -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex ()-retriveExportHttpAlso baseurl key loc dest p = - downloadAction dest p Nothing key (exportLocationUrlAction baseurl loc)+retriveExportHttpAlso :: Maybe URLString -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification+retriveExportHttpAlso baseurl key loc dest p = do+ verifyKeyContentIncrementally AlwaysVerify key $ \iv ->+ downloadAction dest p iv key (exportLocationUrlAction baseurl loc) downloadAction :: FilePath -> MeterUpdate -> Maybe IncrementalVerifier -> Key -> ((URLString -> Annex (Either String ())) -> Annex (Either String ())) -> Annex () downloadAction dest p iv key run =
Remote/Rsync.hs view
@@ -46,6 +46,7 @@ import Utility.Tmp.Dir import Utility.SshHost import Annex.SpecialRemote.Config+import Annex.Verify import qualified Data.Map as M @@ -316,8 +317,11 @@ basedest = fromRawFilePath (fromExportLocation loc) populatedest = liftIO . createLinkOrCopy src -retrieveExportM :: RsyncOpts -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex ()-retrieveExportM o _k loc dest p = rsyncRetrieve o [rsyncurl] dest (Just p)+retrieveExportM :: RsyncOpts -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification+retrieveExportM o k loc dest p =+ verifyKeyContentIncrementally AlwaysVerify k $ \iv ->+ tailVerify iv (toRawFilePath dest) $+ rsyncRetrieve o [rsyncurl] dest (Just p) where rsyncurl = mkRsyncUrl o (fromRawFilePath (fromExportLocation loc))
Remote/S3.hs view
@@ -60,13 +60,13 @@ import Types.ProposedAccepted import Types.NumCopies import Utility.Metered-import Utility.Hash (IncrementalVerifier) import Utility.DataUnits import Annex.Content import qualified Annex.Url as Url import Utility.Url (extractFromResourceT) import Annex.Url (getUrlOptions, withUrlOptions, UrlOptions(..)) import Utility.Env+import Annex.Verify type BucketName = String type BucketObject = String@@ -495,14 +495,14 @@ setS3VersionID info rs k mvid return (metag, mvid) -retrieveExportS3 :: S3HandleVar -> Remote -> S3Info -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex ()-retrieveExportS3 hv r info _k loc f p = do+retrieveExportS3 :: S3HandleVar -> Remote -> S3Info -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification+retrieveExportS3 hv r info k loc f p = verifyKeyContentIncrementally AlwaysVerify k $ \iv -> withS3Handle hv $ \case- Just h -> retrieveHelper info h (Left (T.pack exportloc)) f p Nothing+ Just h -> retrieveHelper info h (Left (T.pack exportloc)) f p iv Nothing -> case getPublicUrlMaker info of Just geturl -> either giveup return =<< Url.withUrlOptions- (Url.download' p Nothing (geturl exportloc) f)+ (Url.download' p iv (geturl exportloc) f) Nothing -> giveup $ needS3Creds (uuid r) where exportloc = bucketExportLocation info loc@@ -649,22 +649,31 @@ | otherwise = i : removemostrecent mtime rest -retrieveExportWithContentIdentifierS3 :: S3HandleVar -> Remote -> RemoteStateHandle -> S3Info -> ExportLocation -> ContentIdentifier -> FilePath -> Annex Key -> MeterUpdate -> Annex Key-retrieveExportWithContentIdentifierS3 hv r rs info loc cid dest mkkey p = withS3Handle hv $ \case- Just h -> do- rewritePreconditionException $ retrieveHelper' h dest p Nothing $- limitGetToContentIdentifier cid $- S3.getObject (bucket info) o- k <- mkkey- case extractContentIdentifier cid o of- Right vid -> do- vids <- getS3VersionID rs k- unless (vid `elem` map Just vids) $- setS3VersionID info rs k vid- Left _ -> noop- return k- Nothing -> giveup $ needS3Creds (uuid r)+retrieveExportWithContentIdentifierS3 :: S3HandleVar -> Remote -> RemoteStateHandle -> S3Info -> ExportLocation -> ContentIdentifier -> FilePath -> Either Key (Annex Key) -> MeterUpdate -> Annex (Key, Verification)+retrieveExportWithContentIdentifierS3 hv r rs info loc cid dest gk p =+ case gk of+ Right _mkkey -> do+ k <- go Nothing+ return (k, UnVerified)+ Left k -> do+ v <- verifyKeyContentIncrementally DefaultVerify k + (void . go)+ return (k, v) where+ go iv = withS3Handle hv $ \case+ Just h -> do+ rewritePreconditionException $ retrieveHelper' h dest p iv $+ limitGetToContentIdentifier cid $+ S3.getObject (bucket info) o+ k <- either return id gk+ case extractContentIdentifier cid o of+ Right vid -> do+ vids <- getS3VersionID rs k+ unless (vid `elem` map Just vids) $+ setS3VersionID info rs k vid+ Left _ -> noop+ return k+ Nothing -> giveup $ needS3Creds (uuid r) o = T.pack $ bucketExportLocation info loc {- Catch exception getObject returns when a precondition is not met,
Remote/WebDAV.hs view
@@ -40,7 +40,7 @@ import Creds import Utility.Metered import Utility.Url (URLString, matchStatusCodeException, matchHttpExceptionContent)-import Utility.Hash (IncrementalVerifier(..))+import Annex.Verify import Annex.UUID import Remote.WebDAV.DavLocation import Types.ProposedAccepted@@ -218,10 +218,11 @@ storeHelper dav (exportTmpLocation loc k) dest reqbody Left err -> giveup err -retrieveExportDav :: DavHandleVar -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex ()-retrieveExportDav hdl _k loc d p = case exportLocation loc of- Right src -> withDavHandle hdl $ \h -> runExport h $ \_dav ->- retrieveHelper src d p Nothing+retrieveExportDav :: DavHandleVar -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification+retrieveExportDav hdl k loc d p = case exportLocation loc of+ Right src -> verifyKeyContentIncrementally AlwaysVerify k $ \iv ->+ withDavHandle hdl $ \h -> runExport h $ \_dav ->+ retrieveHelper src d p iv Left err -> giveup err checkPresentExportDav :: DavHandleVar -> Remote -> Key -> ExportLocation -> Annex Bool
Test.hs view
@@ -252,7 +252,7 @@ {- These tests set up the test environment, but also test some basic parts - of git-annex. They are always run before the repoTests. -} initTests :: TestTree-initTests = testGroup "Init Tests"+initTests = testGroup initTestsName [ testCase "init" test_init , testCase "add" test_add ]@@ -339,7 +339,7 @@ ] where mk l = testGroup groupname (initTests : map adddep l)- adddep = Test.Tasty.after AllSucceed (groupname ++ ".Init Tests")+ adddep = Test.Tasty.after AllSucceed (groupname ++ "." ++ initTestsName) groupname = "Repo Tests " ++ note sep = sep' (replicate numparts []) sep' (p:ps) (l:ls) = sep' (ps++[l:p]) ls@@ -1643,7 +1643,7 @@ git_annex "get" [] "get" annexed_present annexedfile -- any exit status is accepted; does abnormal exit- git_annex' (const True) "uninit" [] "uninit"+ git_annex'' (const True) "uninit" [] Nothing "uninit" checkregularfile annexedfile doesDirectoryExist ".git" @? ".git vanished in uninit" @@ -1758,8 +1758,8 @@ borgdirparent <- fromRawFilePath <$> (absPath . toRawFilePath =<< tmprepodir) let borgdir = borgdirparent </> "borgrepo" intmpclonerepo $ do- testProcess "borg" ["init", borgdir, "-e", "none"] (== True) "borg init"- testProcess "borg" ["create", borgdir++"::backup1", "."] (== True) "borg create"+ testProcess "borg" ["init", borgdir, "-e", "none"] Nothing (== True) "borg init"+ testProcess "borg" ["create", borgdir++"::backup1", "."] Nothing (== True) "borg create" git_annex "initremote" (words $ "borg type=borg borgrepo="++borgdir) "initremote" git_annex "sync" ["borg"] "sync borg"@@ -1769,7 +1769,7 @@ annexed_present annexedfile git_annex_expectoutput "find" ["--in=borg"] [] - testProcess "borg" ["create", borgdir++"::backup2", "."] (== True) "borg create"+ testProcess "borg" ["create", borgdir++"::backup2", "."] Nothing (== True) "borg create" git_annex "sync" ["borg"] "sync borg after getting file" git_annex_expectoutput "find" ["--in=borg"] [annexedfile] @@ -1808,9 +1808,7 @@ let gpgtmp = if length relgpgtmp < length absgpgtmp then relgpgtmp else absgpgtmp- Utility.Gpg.testTestHarness gpgtmp gpgcmd- @? "test harness self-test failed"- void $ Utility.Gpg.testHarness gpgtmp gpgcmd $ do+ void $ Utility.Gpg.testHarness gpgtmp gpgcmd $ \environ -> do createDirectory "dir" let initps = [ "foo"@@ -1821,13 +1819,13 @@ ] ++ if scheme `elem` ["hybrid","pubkey"] then ["keyid=" ++ Utility.Gpg.testKeyId] else []- git_annex "initremote" initps "initremote"- git_annex_shouldfail "initremote" initps "initremote should not work when run twice in a row"- git_annex "enableremote" initps "enableremote"- git_annex "enableremote" initps "enableremote when run twice in a row"- git_annex "get" [annexedfile] "get of file"+ git_annex' "initremote" initps (Just environ) "initremote"+ git_annex_shouldfail' "initremote" initps (Just environ) "initremote should not work when run twice in a row"+ git_annex' "enableremote" initps (Just environ) "enableremote"+ git_annex' "enableremote" initps (Just environ) "enableremote when run twice in a row"+ git_annex' "get" [annexedfile] (Just environ) "get of file" annexed_present annexedfile- git_annex "copy" [annexedfile, "--to", "foo"] "copy --to encrypted remote"+ git_annex' "copy" [annexedfile, "--to", "foo"] (Just environ) "copy --to encrypted remote" (c,k) <- annexeval $ do uuid <- Remote.nameToUUID "foo" rs <- Logs.Remote.readRemoteLog@@ -1836,18 +1834,18 @@ let key = if scheme `elem` ["hybrid","pubkey"] then Just $ Utility.Gpg.KeyIds [Utility.Gpg.testKeyId] else Nothing- testEncryptedRemote scheme key c [k] @? "invalid crypto setup"+ testEncryptedRemote environ scheme key c [k] @? "invalid crypto setup" annexed_present annexedfile- git_annex "drop" [annexedfile, "--numcopies=2"] "drop"+ git_annex' "drop" [annexedfile, "--numcopies=2"] (Just environ) "drop" annexed_notpresent annexedfile- git_annex "move" [annexedfile, "--from", "foo"] "move --from encrypted remote"+ git_annex' "move" [annexedfile, "--from", "foo"] (Just environ) "move --from encrypted remote" annexed_present annexedfile- git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] "drop should not be allowed with numcopies=2"+ git_annex_shouldfail' "drop" [annexedfile, "--numcopies=2"] (Just environ) "drop should not be allowed with numcopies=2" annexed_present annexedfile {- Ensure the configuration complies with the encryption scheme, and - that all keys are encrypted properly for the given directory remote. -}- testEncryptedRemote scheme ks c keys = case Remote.Helper.Encryptable.extractCipher pc of+ testEncryptedRemote environ scheme ks c keys = case Remote.Helper.Encryptable.extractCipher pc of Just cip@Crypto.SharedCipher{} | scheme == "shared" && isNothing ks -> checkKeys cip Nothing Just cip@(Crypto.EncryptedCipher encipher v ks')@@ -1860,18 +1858,18 @@ keysMatch (Utility.Gpg.KeyIds ks') = maybe False (\(Utility.Gpg.KeyIds ks2) -> sort (nub ks2) == sort (nub ks')) ks- checkCipher encipher = Utility.Gpg.checkEncryptionStream gpgcmd encipher . Just+ checkCipher encipher = Utility.Gpg.checkEncryptionStream gpgcmd (Just environ) encipher . Just checkScheme Types.Crypto.Hybrid = scheme == "hybrid" checkScheme Types.Crypto.PubKey = scheme == "pubkey" checkKeys cip mvariant = do dummycfg <- Types.GitConfig.dummyRemoteGitConfig let encparams = (Types.Remote.ParsedRemoteConfig mempty mempty, dummycfg)- cipher <- Crypto.decryptCipher gpgcmd encparams cip+ cipher <- Crypto.decryptCipher' gpgcmd (Just environ) encparams cip files <- filterM doesFileExist $ map ("dir" </>) $ concatMap (serializeKeys cipher) keys return (not $ null files) <&&> allM (checkFile mvariant) files checkFile mvariant filename =- Utility.Gpg.checkEncryptionFile gpgcmd filename $+ Utility.Gpg.checkEncryptionFile gpgcmd (Just environ) filename $ if mvariant == Just Types.Crypto.PubKey then ks else Nothing serializeKeys cipher = map fromRawFilePath . Annex.Locations.keyPaths .
Test/Framework.hs view
@@ -12,10 +12,10 @@ import Test.Tasty import Test.Tasty.Runners import Test.Tasty.HUnit-import Test.Tasty.QuickCheck import Test.Tasty.Options import Test.Tasty.Ingredients.Rerun import Test.Tasty.Ingredients.ConsoleReporter+import qualified Test.Tasty.Patterns.Types as TP import Options.Applicative.Types import Control.Concurrent import Control.Concurrent.Async@@ -24,6 +24,7 @@ import System.Console.Concurrent import System.Console.ANSI import GHC.Conc+import System.IO.Unsafe (unsafePerformIO) import Common import Types.Test@@ -64,32 +65,40 @@ -- Run a process. The output and stderr is captured, and is only -- displayed if the process does not return the expected value.-testProcess :: String -> [String] -> (Bool -> Bool) -> String -> Assertion-testProcess command params expectedret faildesc = do- (transcript, ret) <- Utility.Process.Transcript.processTranscript command params Nothing+testProcess :: String -> [String] -> Maybe [(String, String)] -> (Bool -> Bool) -> String -> Assertion+testProcess command params environ expectedret faildesc = do+ let p = (proc command params) { env = environ }+ (transcript, ret) <- Utility.Process.Transcript.processTranscript' p Nothing (expectedret ret) @? (faildesc ++ " failed (transcript follows)\n" ++ transcript) -- Run git. (Do not use to run git-annex as the one being tested -- may not be in path.) git :: String -> [String] -> String -> Assertion-git command params = testProcess "git" (command:params) (== True)+git command params = testProcess "git" (command:params) Nothing (== True) -- For when git is expected to fail. git_shouldfail :: String -> [String] -> String -> Assertion-git_shouldfail command params = testProcess "git" (command:params) (== False)+git_shouldfail command params = testProcess "git" (command:params) Nothing (== False) -- Run git-annex. git_annex :: String -> [String] -> String -> Assertion-git_annex = git_annex' (== True)+git_annex command params faildesc = git_annex' command params Nothing faildesc +-- Runs git-annex with some environment.+git_annex' :: String -> [String] -> Maybe [(String, String)] -> String -> Assertion+git_annex' = git_annex'' (== True)+ -- For when git-annex is expected to fail. git_annex_shouldfail :: String -> [String] -> String -> Assertion-git_annex_shouldfail = git_annex' (== False)+git_annex_shouldfail command params faildesc = git_annex_shouldfail' command params Nothing faildesc -git_annex' :: (Bool -> Bool) -> String -> [String] -> String -> Assertion-git_annex' expectedret command params faildesc = do+git_annex_shouldfail' :: String -> [String] -> Maybe [(String, String)] -> String -> Assertion+git_annex_shouldfail' = git_annex'' (== False)++git_annex'' :: (Bool -> Bool) -> String -> [String] -> Maybe [(String, String)] -> String -> Assertion+git_annex'' expectedret command params environ faildesc = do pp <- Annex.Path.programPath- testProcess pp (command:params) expectedret faildesc+ testProcess pp (command:params) environ expectedret faildesc {- Runs git-annex and returns its standard output. -} git_annex_output :: String -> [String] -> IO String@@ -252,14 +261,42 @@ e <- doesDirectoryExist d unless e $ createDirectory d- -{- Prevent global git configs from affecting the test suite. -}-isolateGitConfig :: IO a -> IO a-isolateGitConfig a = Utility.Tmp.Dir.withTmpDir "testhome" $ \tmphome -> do++{- This is the only place in the test suite that can use setEnv.+ - Using it elsewhere can conflict with tasty's use of getEnv, which can+ - happen concurrently with a test case running, and would be a problem+ - since setEnv is not thread safe. This is run before tasty. -}+setTestEnv :: IO a -> IO a+setTestEnv a = Utility.Tmp.Dir.withTmpDir "testhome" $ \tmphome -> do tmphomeabs <- fromRawFilePath <$> absPath (toRawFilePath tmphome)+ {- Prevent global git configs from affecting the test suite. -} Utility.Env.Set.setEnv "HOME" tmphomeabs True Utility.Env.Set.setEnv "XDG_CONFIG_HOME" tmphomeabs True Utility.Env.Set.setEnv "GIT_CONFIG_NOSYSTEM" "1" True+ + -- Ensure that the same git-annex binary that is running+ -- git-annex test is at the front of the PATH.+ p <- Utility.Env.getEnvDefault "PATH" ""+ pp <- Annex.Path.programPath+ Utility.Env.Set.setEnv "PATH" (takeDirectory pp ++ [searchPathSeparator] ++ p) True+ + -- Avoid git complaining if it cannot determine the user's+ -- email address, or exploding if it doesn't know the user's name.+ Utility.Env.Set.setEnv "GIT_AUTHOR_EMAIL" "test@example.com" True+ Utility.Env.Set.setEnv "GIT_AUTHOR_NAME" "git-annex test" True+ Utility.Env.Set.setEnv "GIT_COMMITTER_EMAIL" "test@example.com" True+ Utility.Env.Set.setEnv "GIT_COMMITTER_NAME" "git-annex test" True+ -- force gpg into batch mode for the tests+ Utility.Env.Set.setEnv "GPG_BATCH" "1" True+ -- Make git and git-annex access ssh remotes on the local+ -- filesystem, without using ssh at all.+ Utility.Env.Set.setEnv "GIT_SSH_COMMAND" "git-annex test --fakessh --" True+ Utility.Env.Set.setEnv "GIT_ANNEX_USE_GIT_SSH" "1" True++ -- Record top directory.+ currdir <- getCurrentDirectory+ Utility.Env.Set.setEnv "TOPDIR" currdir True+ a removeDirectoryForCleanup :: FilePath -> IO ()@@ -447,7 +484,7 @@ , adjustedUnlockedBranch :: Bool , annexVersion :: Types.RepoVersion.RepoVersion , keepFailures :: Bool- } deriving (Read, Show)+ } deriving (Show) testMode :: TestOptions -> Types.RepoVersion.RepoVersion -> TestMode testMode opts v = TestMode@@ -463,47 +500,32 @@ withTestMode :: TestMode -> TestTree -> TestTree withTestMode testmode = withResource prepare release . const where- prepare = do- setTestMode testmode- setmainrepodir =<< newmainrepodir+ prepare = setTestMode testmode release _ = noop +{- The current test mode is stored here while a test is running.+ -+ - Only one test can be running at a time by a process; running a+ - test also involves chdir into a test repository.+ -}+{-# NOINLINE currentTestMode #-}+currentTestMode :: TMVar TestMode+currentTestMode = unsafePerformIO newEmptyTMVarIO++currentMainRepoDir :: TMVar FilePath+currentMainRepoDir = unsafePerformIO newEmptyTMVarIO+ setTestMode :: TestMode -> IO () setTestMode testmode = do- currdir <- getCurrentDirectory- p <- Utility.Env.getEnvDefault "PATH" ""- pp <- Annex.Path.programPath-- mapM_ (\(var, val) -> Utility.Env.Set.setEnv var val True)- -- Ensure that the same git-annex binary that is running- -- git-annex test is at the front of the PATH.- [ ("PATH", takeDirectory pp ++ [searchPathSeparator] ++ p)- , ("TOPDIR", currdir)- -- Avoid git complaining if it cannot determine the user's- -- email address, or exploding if it doesn't know the user's- -- name.- , ("GIT_AUTHOR_EMAIL", "test@example.com")- , ("GIT_AUTHOR_NAME", "git-annex test")- , ("GIT_COMMITTER_EMAIL", "test@example.com")- , ("GIT_COMMITTER_NAME", "git-annex test")- -- force gpg into batch mode for the tests- , ("GPG_BATCH", "1")- -- Make git and git-annex access ssh remotes on the local- -- filesystem, without using ssh at all.- , ("GIT_SSH_COMMAND", "git-annex test --fakessh --")- , ("GIT_ANNEX_USE_GIT_SSH", "1")- , ("TESTMODE", show testmode)- ]- -runFakeSsh :: [String] -> IO ()-runFakeSsh ("-n":ps) = runFakeSsh ps-runFakeSsh (_host:cmd:[]) =- withCreateProcess (shell cmd) $- \_ _ _ pid -> exitWith =<< waitForProcess pid-runFakeSsh ps = error $ "fake ssh option parse error: " ++ show ps+ atomically $ do+ _ <- tryTakeTMVar currentTestMode+ putTMVar currentTestMode testmode+ setmainrepodir =<< newmainrepodir getTestMode :: IO TestMode-getTestMode = Prelude.read <$> Utility.Env.getEnvDefault "TESTMODE" ""+getTestMode = atomically (tryReadTMVar currentTestMode) >>= \case+ Just tm -> return tm+ Nothing -> error "getTestMode without setTestMode" setupTestMode :: IO () setupTestMode = do@@ -520,13 +542,16 @@ tmpdir :: String tmpdir = ".t" -mainrepodir :: IO FilePath-mainrepodir = Utility.Env.getEnvDefault "MAINREPODIR"- (giveup "MAINREPODIR not set")- setmainrepodir :: FilePath -> IO ()-setmainrepodir d = Utility.Env.Set.setEnv "MAINREPODIR" d True+setmainrepodir mrd = atomically $ do+ _ <- tryTakeTMVar currentMainRepoDir+ putTMVar currentMainRepoDir mrd +mainrepodir :: IO FilePath+mainrepodir = atomically (tryReadTMVar currentMainRepoDir) >>= \case+ Just tm -> return tm+ Nothing -> error "mainrepodir without setmainrepodir"+ newmainrepodir :: IO FilePath newmainrepodir = go (0 :: Int) where@@ -668,6 +693,13 @@ Utility.Process.Transcript.processTranscript "chmod" ["-R", "u+w", d] Nothing +runFakeSsh :: [String] -> IO ()+runFakeSsh ("-n":ps) = runFakeSsh ps+runFakeSsh (_host:cmd:[]) =+ withCreateProcess (shell cmd) $+ \_ _ _ pid -> exitWith =<< waitForProcess pid+runFakeSsh ps = error $ "fake ssh option parse error: " ++ show ps+ {- Tests each TestTree in parallel, and exits with succcess/failure. - - Tasty supports parallel tests, but this does not use it, because@@ -693,10 +725,12 @@ | otherwise = go =<< Utility.Env.getEnv subenv where subenv = "GIT_ANNEX_TEST_SUBPROCESS"+ -- Make more parts than there are jobs, because some parts -- are larger, and this allows the smaller parts to be packed -- in more efficiently, speeding up the test suite overall. numparts = numjobs * 2+ worker rs nvar a = do (n, m) <- atomically $ do (n, m) <- readTVar nvar@@ -707,6 +741,7 @@ else do r <- a n worker (r:rs) nvar a+ go Nothing = withConcurrentOutput $ do ensuredir tmpdir crippledfilesystem <- fst <$> Annex.Init.probeCrippledFileSystem'@@ -722,7 +757,7 @@ args <- getArgs pp <- Annex.Path.programPath termcolor <- hSupportsANSIColor stdout- let ps = if useColor (lookupOption (tastyOptionSet opts)) termcolor+ let ps = if useColor (lookupOption tastyopts) termcolor then "--color=always":args else "--color=never":args let runone n = do@@ -733,13 +768,7 @@ , cwd = Just subdir } (_, _, _, pid) <- createProcessConcurrent p- ret <- waitForProcess pid- -- Work around this strange issue- -- https://github.com/UnkindPartition/tasty/issues/326- -- when other workaround does not work.- if ret == ExitFailure (-11)- then runone n- else return ret+ waitForProcess pid nvar <- newTVarIO (1, length ts) exitcodes <- forConcurrently [1..numjobs] $ \_ -> worker [] nvar runone@@ -755,25 +784,31 @@ exitFailure go (Just subenvval) = case readish subenvval of Nothing -> error ("Bad " ++ subenv)- Just (n, crippledfilesystem, adjustedbranchok) -> isolateGitConfig $ do+ Just (n, crippledfilesystem, adjustedbranchok) -> setTestEnv $ do let ts = mkts numparts crippledfilesystem adjustedbranchok opts- let t = topLevelTestGroup - -- Work around this strange issue- -- https://github.com/UnkindPartition/tasty/issues/326- [ testGroup "Tasty" - [ testProperty "tasty self-check" True- ]- , ts !! (n - 1)- ]- case tryIngredients ingredients (tastyOptionSet opts) t of+ let t = topLevelTestGroup [ ts !! (n - 1) ]+ case tryIngredients ingredients tastyopts t of Nothing -> error "No tests found!?" Just act -> ifM act ( exitSuccess , exitFailure- )+ )+ + tastyopts = case lookupOption (tastyOptionSet opts) of+ -- Work around limitation of tasty; when tests to run+ -- are limited to a pattern, it does not include their+ -- dependencies. So, add another pattern including the+ -- init tests, which are a dependency of most tests.+ TestPattern (Just p) -> + setOption (TestPattern (Just (TP.Or p (TP.ERE initTestsName))))+ (tastyOptionSet opts)+ TestPattern Nothing -> tastyOptionSet opts topLevelTestGroup :: [TestTree] -> TestTree topLevelTestGroup = testGroup "Tests"++initTestsName :: String+initTestsName = "Init Tests" tastyParser :: [TestTree] -> ([String], Parser Test.Tasty.Options.OptionSet) #if MIN_VERSION_tasty(1,3,0)
Types/Remote.hs view
@@ -208,12 +208,15 @@ -- again. The verification does not need to use a -- cryptographically secure hash, but the hash does need to -- have preimage resistance.- | MustVerify- -- ^ Content likely to have been altered during transfer,- -- verify even if verification is normally disabled | IncompleteVerify IncrementalVerifier -- ^ Content was partially verified during transfer, but -- the verification is not complete.+ | MustVerify+ -- ^ Content likely to have been altered during transfer,+ -- verify even if verification is normally disabled+ | MustFinishIncompleteVerify IncrementalVerifier+ -- ^ Content likely to have been altered during transfer,+ -- finish verification even if verification is normally disabled. unVerified :: Monad m => m a -> m (a, Verification) unVerified a = do@@ -262,7 +265,7 @@ -- (The MeterUpdate does not need to be used if it writes -- sequentially to the file.) -- Throws exception on failure.- , retrieveExport :: Key -> ExportLocation -> FilePath -> MeterUpdate -> a ()+ , retrieveExport :: Key -> ExportLocation -> FilePath -> MeterUpdate -> a Verification -- Removes an exported file (succeeds if the contents are not present) -- Can throw exception if unable to access remote, or if remote -- refuses to remove the content.@@ -343,10 +346,11 @@ -> ContentIdentifier -- file to write content to -> FilePath- -- callback that generates a key from the downloaded content- -> a Key+ -- Either the key, or when it's not yet known, a callback+ -- that generates a key from the downloaded content.+ -> Either Key (a Key) -> MeterUpdate- -> a Key+ -> a (Key, Verification) -- Exports content to an ExportLocation, and returns the -- ContentIdentifier corresponding to the content it stored. --
Upgrade/V5/Direct.hs view
@@ -97,7 +97,7 @@ removeAssociatedFiles :: Key -> Annex () removeAssociatedFiles key = do mapping <- calcRepo $ gitAnnexMapping key- modifyContent mapping $+ modifyContentDir mapping $ liftIO $ removeWhenExistsWith R.removeLink mapping {- Checks if a file in the tree, associated with a key, has not been modified.@@ -124,7 +124,7 @@ {- Removes an inode cache. -} removeInodeCache :: Key -> Annex () removeInodeCache key = withInodeCacheFile key $ \f ->- modifyContent f $+ modifyContentDir f $ liftIO $ removeWhenExistsWith R.removeLink f withInodeCacheFile :: Key -> (RawFilePath -> Annex a) -> Annex a
Utility/DataUnits.hs view
@@ -1,6 +1,6 @@ {- data size display and parsing -- - Copyright 2011 Joey Hess <id@joeyh.name>+ - Copyright 2011-2022 Joey Hess <id@joeyh.name> - - License: BSD-2-clause -@@ -22,13 +22,19 @@ - - So, a committee was formed. And it arrived at a committee-like decision, - which satisfied noone, confused everyone, and made the world an uglier- - place. As with all committees, this was meh.+ - place. As with all committees, this was meh. Or in this case, "mib". - - And the drive manufacturers happily continued selling drives that are - increasingly smaller than you'd expect, if you don't count on your - fingers. But that are increasingly too big for anyone to much notice. - This caused me to need git-annex. -+ - Meanwhile, over in telecommunications land, they were using entirely+ - different units that differ only in capitalization sometimes.+ - (At one point this convinced me that it was a good idea to buy an ISDN+ - line because 128 kb/s sounded really fast! But it was really only 128+ - kbit/s...)+ - - Thus, I use units here that I loathe. Because if I didn't, people would - be confused that their drives seem the wrong size, and other people would - complain at me for not being standards compliant. And we call this@@ -38,7 +44,7 @@ module Utility.DataUnits ( dataUnits, storageUnits,- memoryUnits,+ committeeUnits, bandwidthUnits, oldSchoolUnits, Unit(..),@@ -62,7 +68,7 @@ deriving (Ord, Show, Eq) dataUnits :: [Unit]-dataUnits = storageUnits ++ memoryUnits+dataUnits = storageUnits ++ committeeUnits ++ bandwidthUnits {- Storage units are (stupidly) powers of ten. -} storageUnits :: [Unit]@@ -75,15 +81,15 @@ , Unit (p 3) "GB" "gigabyte" , Unit (p 2) "MB" "megabyte" , Unit (p 1) "kB" "kilobyte" -- weird capitalization thanks to committe- , Unit (p 0) "B" "byte"+ , Unit 1 "B" "byte" ] where p :: Integer -> Integer p n = 1000^n -{- Memory units are (stupidly named) powers of 2. -}-memoryUnits :: [Unit]-memoryUnits =+{- Committee units are (stupidly named) powers of 2. -}+committeeUnits :: [Unit]+committeeUnits = [ Unit (p 8) "YiB" "yobibyte" , Unit (p 7) "ZiB" "zebibyte" , Unit (p 6) "EiB" "exbibyte"@@ -92,19 +98,37 @@ , Unit (p 3) "GiB" "gibibyte" , Unit (p 2) "MiB" "mebibyte" , Unit (p 1) "KiB" "kibibyte"- , Unit (p 0) "B" "byte"+ , Unit 1 "B" "byte" ] where p :: Integer -> Integer p n = 2^(n*10) -{- Bandwidth units are only measured in bits if you're some crazy telco. -}+{- Bandwidth units are (stupidly) measured in bits, not bytes, and are+ - (also stupidly) powers of ten. + -+ - While it's fairly common for "Mb", "Gb" etc to be used, that differs+ - from "MB", "GB", etc only in case, and readSize is case-insensitive.+ - So "Mbit", "Gbit" etc are used instead to avoid parsing ambiguity.+ -} bandwidthUnits :: [Unit]-bandwidthUnits = error "stop trying to rip people off"+bandwidthUnits =+ [ Unit (p 8) "Ybit" "yottabit"+ , Unit (p 7) "Zbit" "zettabit"+ , Unit (p 6) "Ebit" "exabit"+ , Unit (p 5) "Pbit" "petabit"+ , Unit (p 4) "Tbit" "terabit"+ , Unit (p 3) "Gbit" "gigabit"+ , Unit (p 2) "Mbit" "megabit"+ , Unit (p 1) "kbit" "kilobit" -- weird capitalization thanks to committe+ ]+ where+ p :: Integer -> Integer+ p n = (1000^n) `div` 8 {- Do you yearn for the days when men were men and megabytes were megabytes? -} oldSchoolUnits :: [Unit]-oldSchoolUnits = zipWith (curry mingle) storageUnits memoryUnits+oldSchoolUnits = zipWith (curry mingle) storageUnits committeeUnits where mingle (Unit _ a n, Unit s' _ _) = Unit s' a n
Utility/Gpg.hs view
@@ -1,6 +1,6 @@ {- gpg interface -- - Copyright 2011 Joey Hess <id@joeyh.name>+ - Copyright 2011-2022 Joey Hess <id@joeyh.name> - - License: BSD-2-clause -}@@ -16,6 +16,7 @@ pkEncTo, stdEncryptionParams, pipeStrict,+ pipeStrict', feedRead, feedRead', findPubKeys,@@ -28,7 +29,6 @@ testKeyId, #ifndef mingw32_HOST_OS testHarness,- testTestHarness, checkEncryptionFile, checkEncryptionStream, #endif@@ -40,7 +40,6 @@ import System.Posix.Types import System.Posix.IO import Utility.Env-import Utility.Env.Set import Utility.FileMode #else import Utility.Tmp@@ -110,10 +109,15 @@ {- Runs gpg with some params and returns its stdout, strictly. -} readStrict :: GpgCmd -> [CommandParam] -> IO String-readStrict (GpgCmd cmd) params = do+readStrict c p = readStrict' c p Nothing++readStrict' :: GpgCmd -> [CommandParam] -> Maybe [(String, String)] -> IO String+readStrict' (GpgCmd cmd) params environ = do params' <- stdParams params let p = (proc cmd params')- { std_out = CreatePipe }+ { std_out = CreatePipe+ , env = environ+ } withCreateProcess p (go p) where go p _ (Just hout) _ pid = do@@ -124,11 +128,15 @@ {- Runs gpg, piping an input value to it, and returning its stdout, - strictly. -} pipeStrict :: GpgCmd -> [CommandParam] -> String -> IO String-pipeStrict (GpgCmd cmd) params input = do+pipeStrict c p i = pipeStrict' c p Nothing i++pipeStrict' :: GpgCmd -> [CommandParam] -> Maybe [(String, String)] -> String -> IO String+pipeStrict' (GpgCmd cmd) params environ input = do params' <- stdParams params let p = (proc cmd params') { std_in = CreatePipe , std_out = CreatePipe+ , env = environ } withCreateProcess p (go p) where@@ -208,11 +216,14 @@ - a key id, or a name; See the section 'HOW TO SPECIFY A USER ID' of - GnuPG's manpage.) -} findPubKeys :: GpgCmd -> String -> IO KeyIds-findPubKeys cmd for+findPubKeys cmd = findPubKeys' cmd Nothing++findPubKeys' :: GpgCmd -> Maybe [(String, String)] -> String -> IO KeyIds+findPubKeys' cmd environ for -- pass forced subkey through as-is rather than -- looking up the master key. | isForcedSubKey for = return $ KeyIds [for]- | otherwise = KeyIds . parse . lines <$> readStrict cmd params+ | otherwise = KeyIds . parse . lines <$> readStrict' cmd params environ where params = [Param "--with-colons", Param "--list-public-keys", Param for] parse = mapMaybe (keyIdField . splitc ':')@@ -410,7 +421,7 @@ - perhaps related to the agent socket), the action is not run, and Nothing - is returned. -}-testHarness :: FilePath -> GpgCmd -> IO a -> IO (Maybe a)+testHarness :: FilePath -> GpgCmd -> ([(String, String)] -> IO a) -> IO (Maybe a) testHarness tmpdir cmd a = ifM (inSearchPath (unGpgCmd cmd)) ( bracket (eitherToMaybe <$> tryNonAsync setup) cleanup go , return Nothing@@ -419,30 +430,30 @@ var = "GNUPGHOME" setup = do- orig <- getEnv var subdir <- makenewdir (1 :: Integer)+ origenviron <- getEnvironment+ let environ = addEntry var subdir origenviron -- gpg is picky about permissions on its home dir liftIO $ void $ tryIO $ modifyFileMode (toRawFilePath subdir) $ removeModes $ otherGroupModes- setEnv var subdir True -- For some reason, recent gpg needs a trustdb to be set up.- _ <- pipeStrict cmd [Param "--trust-model", Param "auto", Param "--update-trustdb"] []- _ <- pipeStrict cmd [Param "--import", Param "-q"] $ unlines+ _ <- pipeStrict' cmd [Param "--trust-model", Param "auto", Param "--update-trustdb"] (Just environ) []+ _ <- pipeStrict' cmd [Param "--import", Param "-q"] (Just environ) $ unlines [testSecretKey, testKey]- return orig+ return environ - cleanup (Just (Just v)) = stopgpgagent >> setEnv var v True- cleanup (Just Nothing) = stopgpgagent >> unsetEnv var- cleanup Nothing = stopgpgagent+ cleanup Nothing = return ()+ cleanup (Just environ) = stopgpgagent environ -- Recent versions of gpg automatically start gpg-agent, or perhaps -- other daemons. Stop them when done. This only affects -- daemons started for the GNUPGHOME that was used. -- Older gpg may not support this, so ignore failure.- stopgpgagent = whenM (inSearchPath "gpgconf") $- void $ boolSystem "gpgconf" [Param "--kill", Param "all"]+ stopgpgagent environ = whenM (inSearchPath "gpgconf") $+ void $ boolSystemEnv "gpgconf" [Param "--kill", Param "all"]+ (Just environ) - go (Just _) = Just <$> a+ go (Just environ) = Just <$> a environ go Nothing = return Nothing makenewdir n = do@@ -451,24 +462,15 @@ createDirectory subdir return subdir -{- Tests the test harness. -}-testTestHarness :: FilePath -> GpgCmd -> IO Bool-testTestHarness tmpdir cmd =- testHarness tmpdir cmd (findPubKeys cmd testKeyId) >>= \case- Nothing -> do- hPutStrLn stderr "unable to test gpg, setting up the test harness did not succeed"- return True- Just keys -> return $ KeyIds [testKeyId] == keys--checkEncryptionFile :: GpgCmd -> FilePath -> Maybe KeyIds -> IO Bool-checkEncryptionFile cmd filename keys =- checkGpgPackets cmd keys =<< readStrict cmd params+checkEncryptionFile :: GpgCmd -> Maybe [(String, String)] -> FilePath -> Maybe KeyIds -> IO Bool+checkEncryptionFile cmd environ filename keys =+ checkGpgPackets cmd environ keys =<< readStrict' cmd params environ where params = [Param "--list-packets", Param "--list-only", File filename] -checkEncryptionStream :: GpgCmd -> String -> Maybe KeyIds -> IO Bool-checkEncryptionStream cmd stream keys =- checkGpgPackets cmd keys =<< pipeStrict cmd params stream+checkEncryptionStream :: GpgCmd -> Maybe [(String, String)] -> String -> Maybe KeyIds -> IO Bool+checkEncryptionStream cmd environ stream keys =+ checkGpgPackets cmd environ keys =<< pipeStrict' cmd params environ stream where params = [Param "--list-packets", Param "--list-only"] @@ -476,8 +478,8 @@ - symmetrically encrypted (keys is Nothing), or encrypted to some - public key(s). - /!\ The key needs to be in the keyring! -}-checkGpgPackets :: GpgCmd -> Maybe KeyIds -> String -> IO Bool-checkGpgPackets cmd keys str = do+checkGpgPackets :: GpgCmd -> Maybe [(String, String)] -> Maybe KeyIds -> String -> IO Bool+checkGpgPackets cmd environ keys str = do let (asym,sym) = partition (pubkeyEncPacket `isPrefixOf`) $ filter (\l' -> pubkeyEncPacket `isPrefixOf` l' || symkeyEncPacket `isPrefixOf` l') $@@ -488,7 +490,7 @@ (Just (KeyIds ks), ls, []) -> do -- Find the master key associated with the -- encryption subkey.- ks' <- concat <$> mapM (keyIds <$$> findPubKeys cmd)+ ks' <- concat <$> mapM (keyIds <$$> findPubKeys' cmd environ) [ k | k:"keyid":_ <- map (reverse . words) ls ] return $ sort (nub ks) == sort (nub ks') _ -> return False
Utility/Metered.hs view
@@ -491,14 +491,14 @@ , estimatedcompletion ] where- amount = roughSize' memoryUnits True 2 new+ amount = roughSize' committeeUnits True 2 new percentamount = case mtotalsize of Just (TotalSize totalsize) -> let p = showPercentage 0 $ percentage totalsize (min new totalsize) in p ++ replicate (6 - length p) ' ' ++ amount Nothing -> amount- rate = roughSize' memoryUnits True 0 bytespersecond ++ "/s"+ rate = roughSize' committeeUnits True 0 bytespersecond ++ "/s" bytespersecond | duration == 0 = fromIntegral transferred | otherwise = floor $ fromIntegral transferred / duration
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 10.20220504+Version: 10.20220525 Cabal-Version: >= 1.10 License: AGPL-3 Maintainer: Joey Hess <id@joeyh.name>