git-annex 10.20250828 → 10.20250925
raw patch · 153 files changed
+894/−535 lines, 153 filesdep −cryptonitedep ~utf8-string
Dependencies removed: cryptonite
Dependency ranges changed: utf8-string
Files
- Annex.hs +2/−0
- Annex/Balanced.hs +1/−2
- Annex/Content.hs +5/−6
- Annex/Content/PointerFile.hs +3/−3
- Annex/Fixup.hs +4/−19
- Annex/Import.hs +46/−24
- Annex/Ingest.hs +2/−2
- Annex/Link.hs +28/−17
- Annex/Locations.hs +0/−6
- Annex/MetaData/StandardFields.hs +0/−2
- Annex/Multicast.hs +19/−4
- Annex/Path.hs +1/−1
- Annex/Sim.hs +4/−5
- Annex/SpecialRemote/Config.hs +17/−7
- Annex/Ssh.hs +1/−1
- Annex/YoutubeDl.hs +1/−1
- Assistant.hs +11/−4
- Assistant/DaemonStatus.hs +1/−1
- Assistant/Gpg.hs +0/−2
- Assistant/Install.hs +4/−4
- Assistant/Install/AutoStart.hs +1/−1
- Assistant/Install/Menu.hs +3/−2
- Assistant/Restart.hs +1/−1
- Assistant/Ssh.hs +11/−9
- Assistant/Threads/Committer.hs +48/−22
- Assistant/Types/BranchChange.hs +0/−2
- Assistant/Upgrade.hs +2/−2
- Assistant/WebApp/Configurators/Local.hs +1/−1
- Assistant/WebApp/Control.hs +1/−1
- Assistant/WebApp/Documentation.hs +1/−1
- Build/Configure.hs +2/−3
- Build/DesktopFile.hs +1/−2
- Build/Mans.hs +7/−7
- Build/TestConfig.hs +9/−3
- Build/Version.hs +3/−5
- BuildFlags.hs +5/−4
- CHANGELOG +32/−0
- COPYRIGHT +6/−1
- Command/Drop.hs +12/−3
- Command/EnableRemote.hs +5/−2
- Command/FuzzTest.hs +1/−1
- Command/Import.hs +6/−6
- Command/ImportFeed.hs +18/−11
- Command/Info.hs +1/−3
- Command/Map.hs +1/−1
- Command/PreCommit.hs +1/−1
- Command/RemoteDaemon.hs +3/−1
- Command/Sim.hs +1/−1
- Command/Smudge.hs +3/−3
- Command/Sync.hs +2/−2
- Command/Unlock.hs +1/−1
- Command/Vicfg.hs +2/−2
- Command/WebApp.hs +1/−1
- Common.hs +1/−0
- Config/Files.hs +1/−1
- Config/Files/AutoStart.hs +2/−2
- Config/Smudge.hs +1/−1
- Crypto.hs +0/−4
- Database/Keys.hs +34/−28
- Database/Keys/Handle.hs +10/−5
- Database/Keys/Tables.hs +1/−5
- Database/Queue.hs +0/−2
- Git/Construct.hs +1/−1
- Git/Fsck.hs +1/−3
- Git/Hook.hs +3/−3
- Git/LockFile.hs +1/−2
- Git/Objects.hs +1/−1
- Git/Quote.hs +1/−3
- Git/Repair.hs +2/−2
- Git/Types.hs +1/−3
- Logs/File.hs +1/−1
- Logs/FsckResults.hs +1/−1
- Logs/Import.hs +45/−9
- Logs/MapLog.hs +1/−3
- Logs/Restage.hs +1/−1
- Logs/Schedule.hs +2/−2
- Logs/Transfer.hs +1/−1
- Logs/View.hs +2/−2
- Messages/JSON.hs +0/−3
- P2P/Http/Server.hs +8/−3
- P2P/Http/State.hs +1/−3
- P2P/Protocol.hs +0/−2
- Remote/Directory.hs +4/−2
- Remote/Directory/LegacyChunked.hs +5/−5
- Remote/Helper/Chunked/Legacy.hs +2/−1
- Remote/Helper/Special.hs +1/−1
- Remote/Tahoe.hs +4/−4
- Test.hs +21/−21
- Test/Framework.hs +6/−5
- Types/CleanupActions.hs +2/−1
- Types/DeferredParse.hs +1/−3
- Types/DesktopNotify.hs +1/−5
- Types/Difference.hs +1/−4
- Types/Distribution.hs +0/−3
- Types/GitConfig.hs +2/−0
- Types/GitRemoteAnnex.hs +1/−2
- Types/Key.hs +0/−2
- Types/Transfer.hs +0/−2
- Types/UUID.hs +1/−2
- Types/VectorClock.hs +0/−2
- Upgrade/V2.hs +2/−2
- Upgrade/V7.hs +1/−1
- Utility/Aeson.hs +0/−1
- Utility/Daemon.hs +16/−9
- Utility/Debug.hs +1/−3
- Utility/DirWatcher/Kqueue.hs +3/−1
- Utility/Directory.hs +0/−3
- Utility/Directory/Create.hs +0/−3
- Utility/Directory/Stream.hs +0/−1
- Utility/Env.hs +0/−1
- Utility/Env/Basic.hs +0/−2
- Utility/FileIO.hs +46/−7
- Utility/FileIO/CloseOnExec.hs +148/−0
- Utility/FileIO/String.hs +38/−0
- Utility/FileMode.hs +1/−1
- Utility/FileSystemEncoding.hs +4/−1
- Utility/FreeDesktop.hs +1/−1
- Utility/Gpg.hs +4/−2
- Utility/Hash.hs +1/−7
- Utility/HumanTime.hs +0/−2
- Utility/IPAddress.hs +0/−2
- Utility/InodeCache.hs +2/−2
- Utility/LinuxMkLibs.hs +0/−2
- Utility/LockFile/PidLock.hs +9/−6
- Utility/LockFile/Posix.hs +4/−5
- Utility/LockPool/LockHandle.hs +0/−1
- Utility/LockPool/PidLock.hs +0/−3
- Utility/LockPool/Posix.hs +0/−4
- Utility/MagicWormhole.hs +0/−2
- Utility/Misc.hs +1/−3
- Utility/MoveFile.hs +0/−1
- Utility/Network.hs +0/−3
- Utility/OpenFd.hs +10/−5
- Utility/OpenFile.hs +1/−1
- Utility/OptParse.hs +0/−2
- Utility/Path.hs +0/−2
- Utility/Path/AbsRel.hs +0/−2
- Utility/Path/Max.hs +0/−3
- Utility/Path/Tests.hs +0/−5
- Utility/Process.hs +34/−5
- Utility/Process/Transcript.hs +5/−4
- Utility/QuickCheck.hs +0/−1
- Utility/SafeCommand.hs +0/−3
- Utility/Scheduled.hs +0/−2
- Utility/Scheduled/QuickCheck.hs +0/−3
- Utility/Shell.hs +2/−1
- Utility/ShellEscape.hs +1/−2
- Utility/StatelessOpenPGP.hs +4/−2
- Utility/Tmp.hs +25/−10
- Utility/Tor.hs +4/−4
- Utility/UserInfo.hs +0/−3
- git-annex.cabal +8/−13
- stack.yaml +0/−1
Annex.hs view
@@ -226,6 +226,7 @@ , cachedgitenv :: Maybe (AltIndexFile, OsPath, [(String, String)]) , urloptions :: Maybe UrlOptions , insmudgecleanfilter :: Bool+ , inreconcilestaged :: Bool , getvectorclock :: IO CandidateVectorClock , proxyremote :: Maybe (Either ClusterUUID (Types.Remote.RemoteA Annex)) , reposizehandle :: Maybe RepoSizeHandle@@ -283,6 +284,7 @@ , cachedgitenv = Nothing , urloptions = Nothing , insmudgecleanfilter = False+ , inreconcilestaged = False , getvectorclock = vc , proxyremote = Nothing , reposizehandle = Nothing
Annex/Balanced.hs view
@@ -12,11 +12,10 @@ import Utility.Hash import Data.Maybe+import Data.List import Data.Bits (shiftL) import qualified Data.Set as S import qualified Data.ByteArray as BA-import Data.List-import Prelude -- The Int is how many UUIDs to pick. type BalancedPicker = S.Set UUID -> Key -> Int -> [UUID]
Annex/Content.hs view
@@ -276,8 +276,7 @@ winLocker takelock _ (Just lockfile) = let lck = do modifyContentDir lockfile $- void $ liftIO $ tryIO $- writeFile (fromOsPath lockfile) ""+ void $ liftIO $ tryIO $ writeFileString lockfile "" liftIO $ takelock lockfile in (lck, Nothing) -- never reached; windows always uses a separate lock file@@ -543,7 +542,7 @@ unless (null fs) $ do destic <- withTSDelta $ liftIO . genInodeCache dest- ics <- mapM (populatePointerFile (Restage True) key dest) fs+ ics <- mapM (populatePointerFile QueueRestage key dest) fs Database.Keys.addInodeCaches key (catMaybes (destic:ics)) )@@ -785,7 +784,7 @@ resetpointer file = unlessM (liftIO $ isSymbolicLink <$> R.getSymbolicLinkStatus (fromOsPath file)) $ ifM (isUnmodified key file) ( adjustedBranchRefresh $ - depopulatePointerFile key file+ depopulatePointerFile QueueRestage key file -- Modified file, so leave it alone. -- If it was a hard link to the annex object, -- that object might have been frozen as part of the@@ -991,7 +990,7 @@ -- clean up gitAnnexTmpWorkDir for those it finds. obj <- prepTmp key unlessM (liftIO $ doesFileExist obj) $ do- liftIO $ writeFile (fromOsPath obj) ""+ liftIO $ writeFileString obj "" setAnnexFilePerm obj let tmpdir = gitAnnexTmpWorkDir obj createAnnexDirectory tmpdir@@ -1083,7 +1082,7 @@ readContentRetentionTimestamp rt >>= \case Just ts | ts >= t -> return () _ -> replaceFile (const noop) rt $ \tmp ->- liftIO $ writeFile (fromOsPath tmp) $ show t+ liftIO $ writeFileString tmp $ show t where lock = takeExclusiveLock unlock = liftIO . dropLock
Annex/Content/PointerFile.hs view
@@ -52,8 +52,8 @@ {- Removes the content from a pointer file, replacing it with a pointer. - - Does not check if the pointer file is modified. -}-depopulatePointerFile :: Key -> OsPath -> Annex ()-depopulatePointerFile key file = do+depopulatePointerFile :: Restage -> Key -> OsPath -> Annex ()+depopulatePointerFile restage key file = do st <- liftIO $ catchMaybeIO $ R.getFileStatus (fromOsPath file) let mode = fmap fileMode st secureErase file@@ -68,4 +68,4 @@ (fmap Posix.modificationTimeHiRes st) #endif withTSDelta (liftIO . genInodeCache tmp)- maybe noop (restagePointerFile (Restage True) file) ic+ maybe noop (restagePointerFile restage file) ic
Annex/Fixup.hs view
@@ -1,6 +1,6 @@ {- git-annex repository fixups -- - Copyright 2013-2020 Joey Hess <id@joeyh.name>+ - Copyright 2013-2025 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -9,29 +9,14 @@ module Annex.Fixup where +import Common import Git.Types import Git.Config import Types.GitConfig-import Utility.Path-import Utility.Path.AbsRel-import Utility.SafeCommand-import Utility.Directory-import Utility.Exception-import Utility.Monad-import Utility.SystemDirectory-import Utility.OsPath import qualified Utility.RawFilePath as R-import Utility.PartialPrelude import qualified Utility.OsString as OS -import System.IO-import Data.List-import Data.Maybe-import Control.Monad-import Control.Monad.IfElse import qualified Data.Map as M-import Control.Applicative-import Prelude fixupRepo :: Repo -> GitConfig -> IO Repo fixupRepo r c = do@@ -132,11 +117,11 @@ -- git-worktree sets up a "commondir" file that contains -- the path to the main git directory. -- Using --separate-git-dir does not.- commondirfile = fromOsPath (d </> literalOsPath "commondir")+ commondirfile = d </> literalOsPath "commondir" readcommondirfile = catchDefaultIO Nothing $ fmap toOsPath . headMaybe . lines- <$> readFile commondirfile+ <$> readFileString commondirfile setworktreepath r' = readcommondirfile >>= \case Just gd -> return $ r'
Annex/Import.hs view
@@ -21,6 +21,7 @@ importKeys, makeImportMatcher, getImportableContents,+ PostExportLogUpdate, ) where import Annex.Common@@ -112,8 +113,9 @@ -> ImportCommitConfig -> AddUnlockedMatcher -> Imported+ -> PostExportLogUpdate -> Annex (Maybe Ref)-buildImportCommit remote importtreeconfig importcommitconfig addunlockedmatcher imported =+buildImportCommit remote importtreeconfig importcommitconfig addunlockedmatcher imported postexportlogupdate = case importCommitTracking importcommitconfig of Nothing -> go Nothing Just trackingcommit -> inRepo (Git.Ref.tree trackingcommit) >>= \case@@ -121,12 +123,14 @@ Just _ -> go (Just trackingcommit) where go trackingcommit = do- (importedtree, updatestate) <- recordImportTree remote importtreeconfig (Just addunlockedmatcher) imported+ (importedtree, updatestate) <- recordImportTree remote importtreeconfig (Just addunlockedmatcher) imported postexportlogupdate buildImportCommit' remote importcommitconfig trackingcommit importedtree >>= \case Just finalcommit -> do updatestate return (Just finalcommit)- Nothing -> return Nothing+ Nothing -> do+ postExportLogUpdate postexportlogupdate+ return Nothing {- Builds a tree for an import from a special remote. -@@ -138,8 +142,9 @@ -> ImportTreeConfig -> Maybe AddUnlockedMatcher -> Imported+ -> PostExportLogUpdate -> Annex (History Sha, Annex ())-recordImportTree remote importtreeconfig addunlockedmatcher imported = do+recordImportTree remote importtreeconfig addunlockedmatcher imported postexportlogupdate = do importedtree@(History finaltree _) <- buildImportTrees basetree subdir addunlockedmatcher imported return (importedtree, updatestate finaltree) where@@ -180,6 +185,7 @@ { oldTreeish = exportedTreeishes oldexport , newTreeish = importedtree }+ postExportLogUpdate postexportlogupdate return oldexport -- importKeys takes care of updating the location log@@ -498,12 +504,27 @@ where ia = Remote.importActions remote --- Result of an import. ImportUnfinished indicates that some file failed to--- be imported. Running again should resume where it left off.+-- Result of an import. data ImportResult t- = ImportFinished t+ = ImportFinished PostExportLogUpdate t | ImportUnfinished+ -- ^ ImportUnfinished indicates that some file failed to+ -- be imported. Running again should resume where it left off. +-- An action to run after the export log has been updated to reflect an+-- import.+newtype PostExportLogUpdate = PostExportLogUpdate (Annex ())++instance Semigroup PostExportLogUpdate where+ PostExportLogUpdate a <> PostExportLogUpdate b =+ PostExportLogUpdate (a >> b)++noPostExportLogUpdate :: PostExportLogUpdate+noPostExportLogUpdate = PostExportLogUpdate (return ())++postExportLogUpdate :: PostExportLogUpdate -> Annex ()+postExportLogUpdate (PostExportLogUpdate a) = a+ data Diffed t = DiffChanged t | DiffRemoved@@ -546,7 +567,10 @@ Nothing -> fullimport currcidtree Just lastimportedtree -> diffimport cidtreemap prevcidtree currcidtree lastimportedtree where- remember = recordContentIdentifierTree (Remote.uuid remote)+ -- Record the content identifier tree after the export log is+ -- updated for the import.+ remember = PostExportLogUpdate .+ recordContentIdentifierTree (Remote.uuid remote) -- In order to use a diff, the previous ContentIdentifier tree must -- not have been garbage collected. Which can happen since there@@ -567,11 +591,11 @@ ) fullimport currcidtree = - importKeys remote importtreeconfig importcontent thirdpartypopulated importablecontents >>= \case- ImportUnfinished -> return ImportUnfinished- ImportFinished r -> do- remember currcidtree- return $ ImportFinished $ ImportedFull r+ importKeys remote importtreeconfig importcontent thirdpartypopulated importablecontents >>= return . \case+ ImportUnfinished -> ImportUnfinished+ ImportFinished a r -> + ImportFinished (a <> remember currcidtree) $+ ImportedFull r diffimport cidtreemap prevcidtree currcidtree lastimportedtree = do (diff, cleanup) <- inRepo $ Git.DiffTree.diffTreeRecursive@@ -589,17 +613,15 @@ ImportUnfinished -> do void $ liftIO cleanup return ImportUnfinished- ImportFinished (ImportableContentsComplete ic') -> - liftIO cleanup >>= \case- False -> return ImportUnfinished- True -> do- remember currcidtree- return $ ImportFinished $ - ImportedDiff lastimportedtree- (mkdiff ic' removed)+ ImportFinished a (ImportableContentsComplete ic') -> + liftIO cleanup >>= return . \case+ False -> ImportUnfinished+ True -> ImportFinished (a <> remember currcidtree) $ + ImportedDiff lastimportedtree+ (mkdiff ic' removed) -- importKeys is not passed ImportableContentsChunked -- above, so it cannot return it- ImportFinished (ImportableContentsChunked {}) -> error "internal"+ ImportFinished _ (ImportableContentsChunked {}) -> error "internal" isremoval ti = Git.DiffTree.dstsha ti `elem` nullShas @@ -685,12 +707,12 @@ ImportableContentsComplete ic -> go False largematcher cidmap importing db ic >>= return . \case Nothing -> ImportUnfinished- Just v -> ImportFinished $ ImportableContentsComplete v+ Just v -> ImportFinished noPostExportLogUpdate $ ImportableContentsComplete v ImportableContentsChunked {} -> do c <- gochunked db (importableContentsChunk importablecontents) gohistory largematcher cidmap importing db (importableHistoryComplete importablecontents) >>= return . \case Nothing -> ImportUnfinished- Just h -> ImportFinished $ ImportableContentsChunked+ Just h -> ImportFinished noPostExportLogUpdate $ ImportableContentsChunked { importableContentsChunk = c , importableHistoryComplete = h }
Annex/Ingest.hs view
@@ -172,7 +172,7 @@ {- Ingests a locked down file into the annex. Does not update the working - tree or the index. -} ingest :: MeterUpdate -> Maybe LockedDown -> Maybe Key -> Annex (Maybe Key, Maybe InodeCache)-ingest meterupdate ld mk = ingest' Nothing meterupdate ld mk (Restage True)+ingest meterupdate ld mk = ingest' Nothing meterupdate ld mk QueueRestage ingest' :: Maybe Backend -> MeterUpdate -> Maybe LockedDown -> Maybe Key -> Restage -> Annex (Maybe Key, Maybe InodeCache) ingest' _ _ Nothing _ _ = return (Nothing, Nothing)@@ -228,7 +228,7 @@ finishIngestUnlocked :: Key -> KeySource -> Annex () finishIngestUnlocked key source = do cleanCruft source- finishIngestUnlocked' key source (Restage True) Nothing+ finishIngestUnlocked' key source QueueRestage Nothing finishIngestUnlocked' :: Key -> KeySource -> Restage -> Maybe LinkAnnexResult -> Annex () finishIngestUnlocked' key source restage lar = do
Annex/Link.hs view
@@ -7,7 +7,7 @@ - - Pointer files are used instead of symlinks for unlocked files. -- - Copyright 2013-2022 Joey Hess <id@joeyh.name>+ - Copyright 2013-2025 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -32,6 +32,7 @@ import Annex.HashObject import Annex.InodeSentinal import Annex.PidLock+import Types.CleanupActions import Utility.FileMode import Utility.InodeCache import Utility.Tmp.Dir@@ -44,8 +45,10 @@ import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Lazy as L+ #ifndef mingw32_HOST_OS #if MIN_VERSION_unix(2,8,0)+import Utility.OpenFd #else import System.PosixCompat.Files (isSymbolicLink) #endif@@ -159,7 +162,10 @@ F.writeFile' file (formatPointer k) maybe noop (R.setFileMode (fromOsPath file)) mode -newtype Restage = Restage Bool+data Restage+ = NoRestage+ | QueueRestage+ | LaterRestage {- Restage pointer file. This is used after updating a worktree file - when content is added/removed, to prevent git status from showing@@ -183,26 +189,27 @@ - and will store it in the restage log. Displays a message to help the - user understand why the file will appear to be modified. -- - This uses the git queue, so the update is not performed immediately,- - and this can be run multiple times cheaply. Using the git queue also- - prevents building up too large a number of updates when many files- - are being processed. It's also recorded in the restage log so that,- - if the process is interrupted before the git queue is fulushed, the- - restage will be taken care of later.+ - The update is not performed immediately, so and this can be run multiple+ - times cheaply. It's also recorded in the restage log so that, if the+ - process is interrupted before the git queue is fulushed, the restage+ - will be taken care of later. -} restagePointerFile :: Restage -> OsPath -> InodeCache -> Annex ()-restagePointerFile (Restage False) f orig = do+restagePointerFile NoRestage f orig = do flip writeRestageLog orig =<< inRepo (toTopFilePath f) toplevelWarning True $ unableToRestage $ Just f-restagePointerFile (Restage True) f orig = do+{- Using the git queue prevents building up too large a number of updates+ - when many files are being processed. -}+restagePointerFile QueueRestage f orig = do flip writeRestageLog orig =<< inRepo (toTopFilePath f)- -- Avoid refreshing the index if run by the- -- smudge clean filter, because git uses that when- -- it's already refreshing the index, probably because- -- this very action is running. Running it again would likely- -- deadlock. unlessM (Annex.getState Annex.insmudgecleanfilter) $ Annex.Queue.addFlushAction restagePointerFileRunner [f]+{- Defer the restage until the end. -}+restagePointerFile LaterRestage f orig = do+ flip writeRestageLog orig =<< inRepo (toTopFilePath f)+ unlessM (Annex.getState Annex.insmudgecleanfilter) $+ Annex.addCleanupAction RestagePointerFiles $ + restagePointerFiles =<< Annex.gitRepo restagePointerFileRunner :: Git.Queue.FlushActionRunner Annex restagePointerFileRunner = @@ -218,7 +225,7 @@ -- to bypass the lock. Then replace the old index file with the new -- updated index file. restagePointerFiles :: Git.Repo -> Annex ()-restagePointerFiles r = unlessM (Annex.getState Annex.insmudgecleanfilter) $ do+restagePointerFiles r = checkcanrun $ do -- Flush any queued changes to the keys database, so they -- are visible to child processes. -- The database is closed because that may improve behavior@@ -329,6 +336,9 @@ ck = ConfigKey "filter.annex.process" ckd = ConfigKey "filter.annex.process-temp-disabled" + checkcanrun a = unlessM (Annex.getState Annex.insmudgecleanfilter) $+ unlessM (Annex.getState Annex.inreconcilestaged) $ a+ unableToRestage :: Maybe OsPath -> StringContainingQuotedPath unableToRestage mf = "git status will show " <> maybe "some files" QuotedPath mf@@ -447,8 +457,9 @@ #else #if MIN_VERSION_unix(2,8,0) let open = do- fd <- openFd (fromOsPath f) ReadOnly + fd <- openFdWithMode (fromOsPath f) ReadOnly Nothing (defaultFileFlags { nofollow = True })+ (CloseOnExecFlag True) fdToHandle fd in bracket open hClose readhandle #else
Annex/Locations.hs view
@@ -47,7 +47,6 @@ gitAnnexUnusedLog, gitAnnexKeysDbDir, gitAnnexKeysDbLock,- gitAnnexKeysDbIndexCache, gitAnnexFsckState, gitAnnexFsckDbDir, gitAnnexFsckDbDirOld,@@ -410,11 +409,6 @@ {- Lock file for the keys database. -} gitAnnexKeysDbLock :: Git.Repo -> GitConfig -> OsPath gitAnnexKeysDbLock r c = gitAnnexKeysDbDir r c <> literalOsPath ".lck"--{- Contains the stat of the last index file that was- - reconciled with the keys database. -}-gitAnnexKeysDbIndexCache :: Git.Repo -> GitConfig -> OsPath-gitAnnexKeysDbIndexCache r c = gitAnnexKeysDbDir r c <> literalOsPath ".cache" {- .git/annex/fsck/uuid/ is used to store information about incremental - fscks. -}
Annex/MetaData/StandardFields.hs view
@@ -22,8 +22,6 @@ import Types.MetaData import qualified Data.Text as T-import Data.Monoid-import Prelude tagMetaField :: MetaField tagMetaField = mkMetaFieldUnchecked "tag"
Annex/Multicast.hs view
@@ -1,18 +1,25 @@ {- git-annex multicast receive callback -- - Copyright 2017 Joey Hess <id@joeyh.name>+ - Copyright 2017-2025 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -} +{-# LANGUAGE CPP #-}+ module Annex.Multicast where import Common import Annex.Path import Utility.Env -import System.Process-import GHC.IO.Handle.FD+#ifndef mingw32_HOST_OS+import System.Posix.IO+#else+import System.Process (createPipeFd)+import GHC.IO.Handle.FD (fdToHandle)+#endif+import GHC.IO.Encoding (getLocaleEncoding) multicastReceiveEnv :: String multicastReceiveEnv = "GIT_ANNEX_MULTICAST_RECEIVE"@@ -20,9 +27,16 @@ multicastCallbackEnv :: IO (OsPath, [(String, String)], Handle) multicastCallbackEnv = do gitannex <- programPath- -- This will even work on Windows+#ifndef mingw32_HOST_OS+ (rfd, wfd) <- noCreateProcessWhile $ do+ (rfd, wfd) <- createPipe+ setFdOption rfd CloseOnExec True+ return (rfd, wfd)+#else (rfd, wfd) <- createPipeFd+#endif rh <- fdToHandle rfd+ getLocaleEncoding >>= hSetEncoding rh environ <- addEntry multicastReceiveEnv (show wfd) <$> getEnvironment return (gitannex, environ, rh) @@ -35,6 +49,7 @@ runMulticastReceive ("-I":_sessionid:fs) hs = case readish hs of Just fd -> do h <- fdToHandle fd+ getLocaleEncoding >>= hSetEncoding h mapM_ (hPutStrLn h) fs hClose h Nothing -> return ()
Annex/Path.hs view
@@ -65,7 +65,7 @@ readProgramFile :: IO (Maybe OsPath) readProgramFile = catchDefaultIO Nothing $ do programfile <- programFile- fmap toOsPath . headMaybe . lines <$> readFile (fromOsPath programfile)+ fmap toOsPath . headMaybe . lines <$> readFileString programfile cannotFindProgram :: IO a cannotFindProgram = do
Annex/Sim.hs view
@@ -1356,16 +1356,15 @@ let st'' = st' { simRepoState = M.map freeze (simRepoState st') }- let statefile = fromOsPath $ - toOsPath (simRootDirectory st'') </> literalOsPath "state"- writeFile statefile (show st'')+ let statefile = toOsPath (simRootDirectory st'') </> literalOsPath "state"+ writeFileString statefile (show st'') where freeze :: SimRepoState SimRepo -> SimRepoState () freeze rst = rst { simRepo = Nothing } restoreSim :: OsPath -> IO (Either String (SimState SimRepo)) restoreSim rootdir = - tryIO (readFile statefile) >>= \case+ tryIO (readFileString statefile) >>= \case Left err -> return (Left (show err)) Right c -> case readMaybe c :: Maybe (SimState ()) of Nothing -> return (Left "unable to parse sim state file")@@ -1379,7 +1378,7 @@ } return (Right st'') where- statefile = fromOsPath $ rootdir </> literalOsPath "state"+ statefile = rootdir </> literalOsPath "state" thaw st (u, rst) = tryNonAsync (thaw' st u) >>= return . \case Left _ -> (u, rst { simRepo = Nothing }) Right r -> (u, rst { simRepo = Just r })
Annex/SpecialRemote/Config.hs view
@@ -206,13 +206,23 @@ getRemoteConfigValue f (ParsedRemoteConfig m _) = case M.lookup f m of Just (RemoteConfigValue v) -> case cast v of Just v' -> Just v'- Nothing -> error $ unwords- [ "getRemoteConfigValue"- , fromProposedAccepted f- , "found value of unexpected type"- , show (typeOf v) ++ "."- , "This is a bug in git-annex!"- ]+ Nothing -> case cast v :: Maybe PassedThrough of+ -- Handle the case where an external special remote+ -- tries to SETCONFIG a value belonging to git-annex,+ -- resulting in a PassedThrough type being stored.+ Just _ -> giveup $ unwords+ [ "Special remote config "+ , fromProposedAccepted f+ , "has been overwritten by SETCONFIG."+ , "This is not supported."+ ]+ Nothing -> error $ unwords+ [ "getRemoteConfigValue"+ , fromProposedAccepted f+ , "found value of unexpected type"+ , show (typeOf v) ++ "."+ , "This is a bug in git-annex!"+ ] Nothing -> Nothing {- Gets all fields that remoteConfigRestPassthrough matched. -}
Annex/Ssh.hs view
@@ -484,4 +484,4 @@ sshAskPassEnv = "GIT_ANNEX_SSHASKPASS" runSshAskPass :: FilePath -> IO ()-runSshAskPass passfile = putStrLn =<< readFile passfile+runSshAskPass passfile = putStrLn =<< readFileString (toOsPath passfile)
Annex/YoutubeDl.hs view
@@ -94,7 +94,7 @@ nofiles = Left $ youtubeDlCommand ++ " did not put any media in its work directory, perhaps it's been configured to store files somewhere else?" toomanyfiles fs = Left $ youtubeDlCommand ++ " downloaded multiple media files; git-annex is only able to deal with one per url: " ++ show fs downloadedfiles = liftIO $ - (nub . lines <$> readFile (fromOsPath filelistfile))+ (nub . lines <$> readFileString filelistfile) `catchIO` (pure . const []) workdirfiles = liftIO $ filter (/= filelistfile) <$> (filterM doesFileExist =<< dirContents workdir)
Assistant.hs view
@@ -56,6 +56,8 @@ #ifdef mingw32_HOST_OS import Utility.Env import System.Environment (getArgs)+#else+import GHC.IO.Encoding (getLocaleEncoding) #endif import qualified Utility.Debug as Debug @@ -82,10 +84,15 @@ let logfd = handleToFd =<< openLog (fromOsPath logfile) if foreground then do- origout <- liftIO $ catchMaybeIO $ - fdToHandle =<< dup stdOutput- origerr <- liftIO $ catchMaybeIO $ - fdToHandle =<< dup stdError+ enc <- liftIO getLocaleEncoding+ origout <- liftIO $ catchMaybeIO $ do+ h <- fdToHandle =<< dup stdOutput+ hSetEncoding h enc+ return h+ origerr <- liftIO $ catchMaybeIO $ do+ h <- fdToHandle =<< dup stdError+ hSetEncoding h enc+ return h let undaemonize = Utility.Daemon.foreground logfd (Just pidfile) start undaemonize $ case startbrowser of
Assistant/DaemonStatus.hs view
@@ -132,7 +132,7 @@ ] readDaemonStatusFile :: FilePath -> IO DaemonStatus-readDaemonStatusFile file = parse <$> newDaemonStatus <*> readFile file+readDaemonStatusFile file = parse <$> newDaemonStatus <*> readFileString (toOsPath file) where parse status = foldr parseline status . lines parseline line status
Assistant/Gpg.hs view
@@ -16,8 +16,6 @@ import Data.Maybe import qualified Data.Map as M-import Control.Applicative-import Prelude {- Generates a gpg user id that is not used by any existing secret key -} newUserId :: GpgCmd -> IO UserId
Assistant/Install.hs view
@@ -57,7 +57,7 @@ let program = base </> literalOsPath "git-annex" programfile <- programFile createDirectoryIfMissing True (parentDir programfile)- writeFile (fromOsPath programfile) (fromOsPath program)+ writeFileString programfile (fromOsPath program) #ifdef darwin_HOST_OS autostartfile <- userAutoStart osxAutoStartLabel@@ -70,7 +70,7 @@ let bootfile = toOsPath home </> literalOsPath ".termux" </> literalOsPath "boot" </> literalOsPath "git-annex" unlessM (doesFileExist bootfile) $ do createDirectoryIfMissing True (takeDirectory bootfile)- writeFile (fromOsPath bootfile) "git-annex assistant --autostart"+ writeFileString bootfile "git-annex assistant --autostart" , do menufile <- desktopMenuFilePath "git-annex" <$> userDataDir icondir <- iconDir <$> userDataDir@@ -125,7 +125,7 @@ userdata <- userDataDir let kdeServiceMenusdir = userdata </> literalOsPath "kservices5" </> literalOsPath "ServiceMenus" createDirectoryIfMissing True kdeServiceMenusdir- writeFile (fromOsPath (kdeServiceMenusdir </> literalOsPath "git-annex.desktop"))+ writeFileString (kdeServiceMenusdir </> literalOsPath "git-annex.desktop") (kdeDesktopFile actions) where genNautilusScript scriptdir action =@@ -136,7 +136,7 @@ ] scriptname action = "git-annex " ++ action installscript f c = whenM (safetoinstallscript f) $ do- writeFile (fromOsPath f) c+ writeFileString f c modifyFileMode f $ addModes [ownerExecuteMode] safetoinstallscript f = catchDefaultIO True $ elem (encodeBS autoaddedcomment) . fileLines'
Assistant/Install/AutoStart.hs view
@@ -23,7 +23,7 @@ installAutoStart command file = do #ifdef darwin_HOST_OS createDirectoryIfMissing True (parentDir file)- writeFile (fromOsPath file) $ genOSXAutoStartFile osxAutoStartLabel command+ writeFileString file $ genOSXAutoStartFile osxAutoStartLabel command ["assistant", "--autostart"] #else writeDesktopMenuFile (fdoAutostart command) file
Assistant/Install/Menu.hs view
@@ -13,6 +13,7 @@ import Common import Utility.FreeDesktop+import qualified Utility.FileIO as F installMenu :: String -> OsPath -> OsPath -> OsPath -> IO () #ifdef darwin_HOST_OS@@ -40,8 +41,8 @@ installIcon :: OsPath -> OsPath -> IO () installIcon src dest = do createDirectoryIfMissing True (parentDir dest)- withBinaryFile (fromOsPath src) ReadMode $ \hin ->- withBinaryFile (fromOsPath dest) WriteMode $ \hout ->+ F.withBinaryFile src ReadMode $ \hin ->+ F.withBinaryFile dest WriteMode $ \hout -> hGetContents hin >>= hPutStr hout iconBaseName :: String
Assistant/Restart.hs view
@@ -79,7 +79,7 @@ r <- Git.Config.read =<< Git.Construct.fromPath repo waiturl $ gitAnnexUrlFile r waiturl urlfile = do- v <- tryIO $ readFile (fromOsPath urlfile)+ v <- tryIO $ readFileString urlfile case v of Left _ -> delayed $ waiturl urlfile Right url -> ifM (assistantListening url)
Assistant/Ssh.hs view
@@ -224,8 +224,8 @@ unless ok $ giveup "ssh-keygen failed" SshKeyPair- <$> readFile (fromOsPath (dir </> literalOsPath "key.pub"))- <*> readFile (fromOsPath (dir </> literalOsPath "key"))+ <$> readFileString (dir </> literalOsPath "key.pub")+ <*> readFileString (dir </> literalOsPath "key") {- Installs a ssh key pair, and sets up ssh config with a mangled hostname - that will enable use of the key. This way we avoid changing the user's@@ -255,7 +255,7 @@ writeFileProtected (sshdir </> sshPrivKeyFile sshdata) (sshPrivKey sshkeypair) unlessM (doesFileExist $ sshdir </> sshPubKeyFile sshdata) $- writeFile (fromOsPath (sshdir </> sshPubKeyFile sshdata))+ writeFileString (sshdir </> sshPubKeyFile sshdata) (sshPubKey sshkeypair) setSshConfig sshdata@@ -277,8 +277,10 @@ setupSshKeyPair :: SshData -> IO (SshData, SshKeyPair) setupSshKeyPair sshdata = do sshdir <- sshDir- mprivkey <- catchMaybeIO $ readFile (fromOsPath (sshdir </> sshPrivKeyFile sshdata))- mpubkey <- catchMaybeIO $ readFile (fromOsPath (sshdir </> sshPubKeyFile sshdata))+ mprivkey <- catchMaybeIO $ readFileString+ (sshdir </> sshPrivKeyFile sshdata)+ mpubkey <- catchMaybeIO $ readFileString+ (sshdir </> sshPubKeyFile sshdata) keypair <- case (mprivkey, mpubkey) of (Just privkey, Just pubkey) -> return $ SshKeyPair { sshPubKey = pubkey@@ -330,15 +332,15 @@ setSshConfig sshdata config = do sshdir <- sshDir createDirectoryIfMissing True sshdir- let configfile = fromOsPath (sshdir </> literalOsPath "config")- unlessM (catchBoolIO $ isInfixOf mangledhost <$> readFile configfile) $ do- appendFile configfile $ unlines $+ let configfile = sshdir </> literalOsPath "config"+ unlessM (catchBoolIO $ isInfixOf mangledhost <$> readFileString configfile) $ do+ appendFileString configfile $ unlines $ [ "" , "# Added automatically by git-annex" , "Host " ++ mangledhost ] ++ map (\(k, v) -> "\t" ++ k ++ " " ++ v) (settings ++ config)- setSshConfigMode (toOsPath configfile)+ setSshConfigMode configfile return $ sshdata { sshHostName = T.pack mangledhost
Assistant/Threads/Committer.hs view
@@ -62,6 +62,11 @@ fmap Seconds . annexDelayAdd <$> Annex.getGitConfig largefilematcher <- liftAnnex largeFilesMatcher annexdotfiles <- liftAnnex $ getGitConfigVal annexDotFiles+ addunlockedmatcher <- liftAnnex $+ ifM (annexSupportUnlocked <$> Annex.getGitConfig)+ ( Just <$> addUnlockedMatcher+ , return Nothing+ ) msg <- liftAnnex Command.Sync.commitMsg lockdowndir <- liftAnnex $ fromRepo gitAnnexTmpWatcherDir liftAnnex $ do@@ -70,7 +75,7 @@ void $ liftIO $ tryIO $ removeDirectoryRecursive lockdowndir void $ createAnnexDirectory lockdowndir waitChangeTime $ \(changes, time) -> do- readychanges <- handleAdds lockdowndir havelsof largefilematcher annexdotfiles delayadd $+ readychanges <- handleAdds lockdowndir havelsof largefilematcher annexdotfiles addunlockedmatcher delayadd $ simplifyChanges changes if shouldCommit False time (length readychanges) readychanges then do@@ -275,8 +280,8 @@ - Any pending adds that are not ready yet are put back into the ChangeChan, - where they will be retried later. -}-handleAdds :: OsPath -> Bool -> GetFileMatcher -> Bool -> Maybe Seconds -> [Change] -> Assistant [Change]-handleAdds lockdowndir havelsof largefilematcher annexdotfiles delayadd cs = returnWhen (null incomplete) $ do+handleAdds :: OsPath -> Bool -> GetFileMatcher -> Bool -> Maybe AddUnlockedMatcher -> Maybe Seconds -> [Change] -> Assistant [Change]+handleAdds lockdowndir havelsof largefilematcher annexdotfiles addunlockedmatcher delayadd cs = returnWhen (null incomplete) $ do let (pending, inprocess) = partition isPendingAddChange incomplete let lockdownconfig = LockDownConfig { lockingFile = False@@ -340,9 +345,9 @@ Command.Add.addFile Command.Add.Small f =<< liftIO (R.getSymbolicLinkStatus (fromOsPath f)) - {- Avoid overhead of re-injesting a renamed unlocked file, by- - examining the other Changes to see if a removed file has the- - same InodeCache as the new file. If so, we can just update+ {- When adding the file unlocked, avoid overhead of re-injesting a renamed+ - unlocked file, by examining the other Changes to see if a removed+ - file has the same InodeCache as the new file. If so, we can just update - bookkeeping, and stage the file in git. -} addannexed :: [Change] -> Assistant [Maybe Change]@@ -357,18 +362,36 @@ , checkWritePerms = True } if M.null m- then forM toadd (addannexed' cfg)+ then forM toadd $ \c -> do+ mcache <- liftIO $ genInodeCache (changeFile c) delta+ addunlocked <- checkaddunlocked c+ addannexed' cfg c addunlocked mcache else forM toadd $ \c -> do mcache <- liftIO $ genInodeCache (changeFile c) delta- case mcache of- Nothing -> addannexed' cfg c- Just cache ->- case M.lookup (inodeCacheToKey ct cache) m of- Nothing -> addannexed' cfg c- Just k -> fastadd c k+ ifM (checkaddunlocked c)+ ( case mcache of+ Nothing -> addannexed' cfg c True Nothing+ Just cache ->+ case M.lookup (inodeCacheToKey ct cache) m of+ Nothing -> addannexed' cfg c True Nothing+ Just k -> fastadd c k+ , addannexed' cfg c False mcache+ ) - addannexed' :: LockDownConfig -> Change -> Assistant (Maybe Change)- addannexed' lockdownconfig change@(InProcessAddChange { lockedDown = ld }) = + checkaddunlocked (InProcessAddChange { lockedDown = ld }) = + case addunlockedmatcher of+ Just addunlockedmatcher' -> do+ let mi = MatchingFile $ FileInfo+ { contentFile = contentLocation (keySource ld)+ , matchFile = keyFilename (keySource ld)+ , matchKey = Nothing+ }+ liftAnnex $ addUnlocked addunlockedmatcher' mi True+ Nothing -> return True+ checkaddunlocked _ = return True++ addannexed' :: LockDownConfig -> Change -> Bool -> Maybe InodeCache -> Assistant (Maybe Change)+ addannexed' lockdownconfig change@(InProcessAddChange { lockedDown = ld }) addunlocked mcache = catchDefaultIO Nothing <~> doadd where ks = keySource ld@@ -376,14 +399,14 @@ (mkey, _mcache) <- liftAnnex $ do showStartMessage (StartMessage "add" (ActionItemOther (Just (QuotedPath (keyFilename ks)))) (SeekInput [])) ingest nullMeterUpdate (Just $ LockedDown lockdownconfig ks) Nothing- maybe (failedingest change) (done change $ keyFilename ks) mkey- addannexed' _ _ = return Nothing+ maybe (failedingest change) (done change addunlocked mcache $ keyFilename ks) mkey+ addannexed' _ _ _ _ = return Nothing fastadd :: Change -> Key -> Assistant (Maybe Change) fastadd change key = do let source = keySource $ lockedDown change liftAnnex $ finishIngestUnlocked key source- done change (keyFilename source) key+ done change True Nothing (keyFilename source) key removedKeysMap :: InodeComparisonType -> [Change] -> Annex (M.Map InodeCacheKey Key) removedKeysMap ct l = do@@ -399,11 +422,14 @@ liftAnnex showEndFail return Nothing - done change file key = liftAnnex $ do+ done change addunlocked mcache file key = liftAnnex $ do logStatus NoLiveUpdate key InfoPresent- mode <- liftIO $ catchMaybeIO $- fileMode <$> R.getFileStatus (fromOsPath file)- stagePointerFile file mode =<< hashPointerFile key+ if addunlocked+ then do+ mode <- liftIO $ catchMaybeIO $+ fileMode <$> R.getFileStatus (fromOsPath file)+ stagePointerFile file mode =<< hashPointerFile key+ else addSymlink file key mcache showEndOk return $ Just $ finishedChange change key
Assistant/Types/BranchChange.hs view
@@ -8,8 +8,6 @@ module Assistant.Types.BranchChange where import Control.Concurrent.MSampleVar-import Control.Applicative-import Prelude newtype BranchChangeHandle = BranchChangeHandle (MSampleVar ())
Assistant/Upgrade.hs view
@@ -160,7 +160,7 @@ unlessM (boolSystem (fromOsPath program) [Param "version"]) $ giveup "New git-annex program failed to run! Not using." pf <- programFile- liftIO $ writeFile (fromOsPath pf) (fromOsPath program)+ liftIO $ writeFileString pf (fromOsPath program) #ifdef darwin_HOST_OS {- OS X uses a dmg, so mount it, and copy the contents into place. -}@@ -281,7 +281,7 @@ deleteFromManifest :: OsPath -> IO () deleteFromManifest dir = do fs <- map (\f -> dir </> toOsPath f) . lines - <$> catchDefaultIO "" (readFile (fromOsPath manifest))+ <$> catchDefaultIO "" (readFileString manifest) mapM_ (removeWhenExistsWith removeFile) fs removeWhenExistsWith removeFile manifest removeEmptyRecursive dir
Assistant/WebApp/Configurators/Local.hs view
@@ -185,7 +185,7 @@ where addignore = do liftIO $ unlessM (doesFileExist $ literalOsPath ".gitignore") $- writeFile ".gitignore" ".thumbnails"+ writeFileString (literalOsPath ".gitignore") ".thumbnails" void $ inRepo $ Git.Command.runBool [Param "add", File ".gitignore"]
Assistant/WebApp/Control.hs view
@@ -74,5 +74,5 @@ getLogR = page "Logs" Nothing $ do logfile <- liftAnnex $ fromRepo gitAnnexDaemonLogFile logs <- liftIO $ listLogs (fromOsPath logfile)- logcontent <- liftIO $ concat <$> mapM readFile logs+ logcontent <- liftIO $ concat <$> mapM (readFileString . toOsPath) logs $(widgetFile "control/log")
Assistant/WebApp/Documentation.hs view
@@ -34,7 +34,7 @@ Just f -> customPage (Just About) $ do -- no sidebar, just pages of legalese.. setTitle "License"- license <- liftIO $ readFile (fromOsPath f)+ license <- liftIO $ readFileString f $(widgetFile "documentation/license") getRepoGroupR :: Handler Html
Build/Configure.hs view
@@ -12,10 +12,9 @@ import qualified Git.Version import Utility.SystemDirectory import Utility.OsPath+import qualified Utility.FileIO as F import Control.Monad-import Control.Applicative-import Prelude tests :: [TestCase] tests =@@ -93,7 +92,7 @@ setup :: IO () setup = do createDirectoryIfMissing True (toOsPath tmpDir)- writeFile testFile "test file contents"+ F.writeFileString (toOsPath testFile) "test file contents" cleanup :: IO () cleanup = removeDirectoryRecursive (toOsPath tmpDir)
Build/DesktopFile.hs view
@@ -22,7 +22,6 @@ import System.Environment #ifndef mingw32_HOST_OS import System.Posix.User-import Prelude #endif systemwideInstall :: IO Bool@@ -74,7 +73,7 @@ , do programfile <- inDestDir =<< programFile createDirectoryIfMissing True (parentDir programfile)- writeFile (fromOsPath programfile) command+ writeFileString programfile command ) installUser :: FilePath -> IO ()
Build/Mans.hs view
@@ -1,6 +1,6 @@ {- Build man pages. -- - Copyright 2016 Joey Hess <id@joeyh.name>+ - Copyright 2016-2025 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -9,16 +9,14 @@ module Build.Mans where +import Utility.Process import System.Directory import System.FilePath import Data.List import Control.Monad-import System.Process import System.Exit import Data.Maybe import Utility.Exception-import Control.Applicative-import Prelude buildMansOrWarn :: IO () buildMansOrWarn = do@@ -37,15 +35,17 @@ destm <- catchMaybeIO $ getModificationTime dest if (Just srcm > destm) then do- r <- system $ unwords- -- Run with per because in some+ (Nothing, Nothing, Nothing, pid) <- createProcess $ shell $ unwords $+ -- Run with perl because in some -- cases it may not be executable.- [ "perl", "./Build/mdwn2man"+ [ "perl"+ , "./Build/mdwn2man" , progName src , "1" , src , "> " ++ dest ]+ r <- waitForProcess pid if r == ExitSuccess then return (Just dest) else return Nothing
Build/TestConfig.hs view
@@ -1,6 +1,7 @@ {- Tests the system and generates SysConfig. -} {-# OPTIONS_GHC -fno-warn-tabs #-}+{-# LANGUAGE OverloadedStrings #-} module Build.TestConfig where @@ -9,6 +10,7 @@ import Utility.SafeCommand import Utility.SystemDirectory import Utility.OsPath+import qualified Utility.FileIO as F import System.IO @@ -42,7 +44,7 @@ valuetype (MaybeBoolConfig _) = "Maybe Bool" writeSysConfig :: [Config] -> IO ()-writeSysConfig config = writeFile "Build/SysConfig" body+writeSysConfig config = F.writeFileString (literalOsPath "Build/SysConfig") body where body = unlines $ header ++ map show config ++ footer header = [@@ -100,12 +102,16 @@ ifM (inSearchPath command) ( return $ Config k $ MaybeStringConfig $ Just command , do- r <- getM find ["/usr/sbin", "/sbin", "/usr/local/sbin"]+ r <- getM find+ [ literalOsPath "/usr/sbin"+ , literalOsPath "/sbin"+ , literalOsPath "/usr/local/sbin"+ ] return $ Config k $ MaybeStringConfig r ) where find d =- let f = toOsPath d </> toOsPath command+ let f = d </> toOsPath command in ifM (doesFileExist f) ( return (Just (fromOsPath f)) , return Nothing
Build/Version.hs view
@@ -8,14 +8,12 @@ import Data.List import System.Environment import Data.Char-import System.Process-import Control.Applicative-import Prelude import Utility.Monad import Utility.Exception import Utility.OsPath import Utility.FileSystemEncoding+import Utility.Process import qualified Utility.FileIO as F type Version = String@@ -39,7 +37,7 @@ gitversion <- takeWhile (\c -> isAlphaNum c) <$> readProcess "sh" [ "-c" , "git log -n 1 --format=format:'%H'"- ] ""+ ] return $ if null gitversion then changelogversion else concat@@ -51,7 +49,7 @@ getChangelogVersion :: IO Version getChangelogVersion = do- changelog <- readFile "CHANGELOG"+ changelog <- F.readFileString (literalOsPath "CHANGELOG") let verline = takeWhile (/= '\n') changelog return $ middle (words verline !! 1) where
BuildFlags.hs view
@@ -69,6 +69,11 @@ , "Testsuite" , "S3" , "WebDAV"+#ifdef WITH_OSPATH+ , "OsPath"+#else +#warning Building without the OsPath build flag set results in slower filename manipulation and is not recommended.+#endif ] -- Not a complete list, let alone a listing transitive deps, but only@@ -80,11 +85,7 @@ , ("bloomfilter", VERSION_bloomfilter) , ("http-client", VERSION_http_client) , ("persistent-sqlite", VERSION_persistent_sqlite)-#ifdef WITH_CRYPTON , ("crypton", VERSION_crypton)-#else- , ("cryptonite", VERSION_cryptonite)-#endif , ("aws", VERSION_aws) , ("DAV", VERSION_DAV) #ifdef WITH_TORRENTPARSER
CHANGELOG view
@@ -1,3 +1,35 @@+git-annex (10.20250925) upstream; urgency=medium++ * Fix bug that made changes to a special remote sometimes be missed when+ importing a tree from it. After upgrading, any such missed changes+ will be included in the next tree imported from a special remote.+ Fixes reversion introduced in version 10.20230626.+ * Fix crash operating on filenames that are exactly 21 bytes long+ and begin with a utf-8 character.+ * Fix hang that could occur when using git-annex adjust on a branch with+ a number of files greater than annex.queuesize.+ * Fix bug that could cause an invalid utf-8 sequence to be used in a+ temporary filename when the input filename was valid utf-8.+ * Improve performance when used with a local git remote that has a+ large working tree.+ * drop: --fast support when dropping from a remote.+ * Added annex.assistant.allowunlocked config.+ * Add git-remote-p2p-annex and git-remote-tor-annex to standalone builds.+ * enableremote: Disallow using type= to attempt to change the type of an+ existing remote.+ * Add build warnings when git-annex is built without the OsPath+ build flag.+ * version: Report on whether it was built with the OsPath build flag.+ * Avoid leaking file descriptors to child processes started by git-annex+ in some situations. Note that when not built with the OsPath build+ flag, these leaks can still happen.+ * git-annex.cabal: Turn on the OsPath build flag by default.+ * p2phttp: Fix a hang that could occur when used with --directory,+ and a repository in the directory got removed.+ * Removed support for building with unmaintained cryptonite, use crypton.++ -- Joey Hess <id@joeyh.name> Thu, 25 Sep 2025 13:20:38 -0400+ git-annex (10.20250828) upstream; urgency=medium * p2p: Added --enable option, which can be used to enable P2P networks
COPYRIGHT view
@@ -42,6 +42,11 @@ 2007-2015 Bryan O'Sullivan License: BSD-3-clause +Files: Utility/FileIO/CloseOnExec.hs+Copyright: 2025 Joey Hess <id@joeyh.name>+ 2024 Julian Ospald+License: BSD-3-clause+ Files: Utility/Matcher.hs Utility/Tor.hs Utility/Yesod.hs Copyright: © 2010-2023 Joey Hess <id@joeyh.name> License: AGPL-3+@@ -144,7 +149,7 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. .- 3. Neither the name of the author nor the names of his contributors+ 3. Neither the name of the author nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. .
Command/Drop.hs view
@@ -1,6 +1,6 @@ {- git-annex command -- - Copyright 2010-2021 Joey Hess <id@joeyh.name>+ - Copyright 2010-2025 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -109,8 +109,17 @@ performLocal lu pcc key afile numcopies mincopies preverified ud startRemote :: LiveUpdate -> PreferredContentChecked -> AssociatedFile -> ActionItem -> SeekInput -> NumCopies -> MinCopies -> Key -> DroppingUnused -> Remote -> CommandStart-startRemote lu pcc afile ai si numcopies mincopies key ud remote = - starting "drop" (OnlyActionOn key ai) si $ do+startRemote lu pcc afile ai si numcopies mincopies key ud remote = do+ fast <- Annex.getRead Annex.fast+ if fast+ then do+ remotes <- Remote.keyPossibilities (Remote.IncludeIgnored True) key+ if remote `elem` remotes+ then go+ else stop+ else go+ where+ go = starting "drop" (OnlyActionOn key ai) si $ do showAction $ UnquotedString $ "from " ++ Remote.name remote performRemote lu pcc key afile numcopies mincopies remote ud
Command/EnableRemote.hs view
@@ -59,8 +59,11 @@ start o (name:rest) = go =<< filter matchingname <$> Annex.getGitRemotes where matchingname r = Git.remoteName r == Just name- go [] = deadLast name $ - startSpecialRemote o name (Logs.Remote.keyValToConfig Proposed rest)+ go [] = deadLast name $+ let config = Logs.Remote.keyValToConfig Proposed rest+ in case M.lookup SpecialRemote.typeField config of+ Nothing -> startSpecialRemote o name config+ Just _ -> giveup "Cannot change type= of existing special remote. Instead, use: git-annex initremote --sameas" go (r:_) | not (null rest) = go [] | otherwise = do
Command/FuzzTest.hs view
@@ -178,7 +178,7 @@ runFuzzAction (FuzzAdd (FuzzFile f)) = do createWorkTreeDirectory (parentDir (toOsPath f)) n <- liftIO (getStdRandom random :: IO Int)- liftIO $ writeFile f $ show n ++ "\n"+ liftIO $ writeFileString (toOsPath f) $ show n ++ "\n" runFuzzAction (FuzzDelete (FuzzFile f)) = liftIO $ removeWhenExistsWith removeFile (toOsPath f) runFuzzAction (FuzzMove (FuzzFile src) (FuzzFile dest)) = liftIO $
Command/Import.hs view
@@ -349,9 +349,9 @@ , Remote.name remote , ". Re-run command to resume import." ]- ImportFinished imported -> void $- includeCommandAction $ - commitimport imported+ ImportFinished postexportlogupdate imported ->+ void $ includeCommandAction $ + commitimport imported postexportlogupdate where importmessages' | null importmessages = ["import from " ++ Remote.name remote]@@ -383,10 +383,10 @@ , err ] -commitRemote :: Remote -> Branch -> RemoteTrackingBranch -> Maybe Sha -> ImportTreeConfig -> ImportCommitConfig -> AddUnlockedMatcher -> Imported -> CommandStart-commitRemote remote branch tb trackingcommit importtreeconfig importcommitconfig addunlockedmatcher imported =+commitRemote :: Remote -> Branch -> RemoteTrackingBranch -> Maybe Sha -> ImportTreeConfig -> ImportCommitConfig -> AddUnlockedMatcher -> Imported -> PostExportLogUpdate -> CommandStart+commitRemote remote branch tb trackingcommit importtreeconfig importcommitconfig addunlockedmatcher imported postexportlogupdate = starting "update" ai si $ do- importcommit <- buildImportCommit remote importtreeconfig importcommitconfig addunlockedmatcher imported+ importcommit <- buildImportCommit remote importtreeconfig importcommitconfig addunlockedmatcher imported postexportlogupdate next $ updateremotetrackingbranch importcommit where ai = ActionItemOther (Just $ UnquotedString $ fromRef $ fromRemoteTrackingBranch tb)
Command/ImportFeed.hs view
@@ -1,6 +1,6 @@ {- git-annex command -- - Copyright 2013-2024 Joey Hess <id@joeyh.name>+ - Copyright 2013-2025 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -22,6 +22,7 @@ import Data.Time.Calendar import Data.Time.LocalTime import Control.Concurrent.STM+import Codec.Binary.UTF8.String (decodeString) import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.ByteString as B@@ -48,6 +49,7 @@ import Annex.MetaData import Annex.FileMatcher import Annex.UntrustedFilePath+import qualified Utility.FileIO as F import qualified Utility.RawFilePath as R import qualified Database.ImportFeed as Db @@ -158,19 +160,16 @@ | otherwise = get get = withTmpFile (literalOsPath "feed") $ \tmpf h -> do- let tmpf' = fromRawFilePath $ fromOsPath tmpf liftIO $ hClose h- ifM (downloadFeed url tmpf')- ( parse tmpf'+ ifM (downloadFeed url tmpf)+ ( parse tmpf , do recordfail next $ feedProblem url "downloading the feed failed" ) - -- Use parseFeedFromFile rather than reading the file- -- ourselves because it goes out of its way to handle encodings.- parse tmpf = liftIO (parseFeedFromFile tmpf) >>= \case+ parse tmpf = liftIO (parseFeedFromFile' tmpf) >>= \case Nothing -> debugfeedcontent tmpf "parsing the feed failed" Just f -> do case decodeBS $ fromFeedText $ getFeedTitle f of@@ -183,7 +182,7 @@ next $ return True debugfeedcontent tmpf msg = do- feedcontent <- liftIO $ readFile tmpf+ feedcontent <- liftIO $ readFileString tmpf fastDebug "Command.ImportFeed" $ unlines [ "start of feed content" , feedcontent@@ -265,11 +264,11 @@ } {- Feeds change, so a feed download cannot be resumed. -}-downloadFeed :: URLString -> FilePath -> Annex Bool+downloadFeed :: URLString -> OsPath -> Annex Bool downloadFeed url f | Url.parseURIRelaxed url == Nothing = giveup "invalid feed url" | otherwise = Url.withUrlOptions Nothing $- Url.download nullMeterUpdate Nothing url (toOsPath f)+ Url.download nullMeterUpdate Nothing url f startDownload :: AddUnlockedMatcher -> ImportFeedOptions -> Cache -> TMVar Bool -> ToDownload -> CommandStart startDownload addunlockedmatcher opts cache cv todownload = case location todownload of@@ -611,7 +610,7 @@ checkFeedBroken' :: URLString -> OsPath -> Annex Bool checkFeedBroken' url f = do prev <- maybe Nothing readish- <$> liftIO (catchMaybeIO $ readFile (fromOsPath f))+ <$> liftIO (catchMaybeIO $ readFileString f) now <- liftIO getCurrentTime case prev of Nothing -> do@@ -645,3 +644,11 @@ -} fromFeedText :: T.Text -> B.ByteString fromFeedText = TE.encodeUtf8++{- Like Test.Feed.parseFeedFromFile, but ensures the close-on-exec bit is+ - set when opening the file. -}+parseFeedFromFile' :: OsPath -> IO (Maybe Feed)+parseFeedFromFile' fp = parseFeedString <$> utf8readfile fp+ where+ utf8readfile :: OsPath -> IO String+ utf8readfile f = fmap decodeString (hGetContents =<< F.openBinaryFile f ReadMode)
Command/Info.hs view
@@ -17,8 +17,6 @@ import Data.ByteString.Short (fromShort) import System.PosixCompat.Files (isDirectory) import Data.Ord-import qualified Data.Semigroup as Sem-import Prelude import Command import qualified Git@@ -68,7 +66,7 @@ , backendsKeys :: M.Map KeyVariety Integer } -instance Sem.Semigroup KeyInfo where+instance Semigroup KeyInfo where a <> b = KeyInfo { countKeys = countKeys a + countKeys b , sizeKeys = sizeKeys a + sizeKeys b
Command/Map.hs view
@@ -55,7 +55,7 @@ <$> fromRepo gitAnnexDir <*> pure (literalOsPath "map.dot") - liftIO $ writeFile (fromOsPath file) (drawMap rs trustmap umap)+ liftIO $ writeFileString file (drawMap rs trustmap umap) next $ ifM (Annex.getRead Annex.fast) ( runViewer file []
Command/PreCommit.hs view
@@ -42,7 +42,7 @@ =<< isAnnexLink f -- after a merge conflict or git cherry-pick or stash, pointer -- files in the worktree won't be populated, so populate them here- Command.Smudge.updateSmudged (Restage False)+ Command.Smudge.updateSmudged NoRestage runAnnexHook preCommitAnnexHook annexPreCommitCommand
Command/RemoteDaemon.hs view
@@ -31,7 +31,9 @@ #ifndef mingw32_HOST_OS git_annex <- fromOsPath <$> liftIO programPath ps <- gitAnnexDaemonizeParams- let logfd = openFdWithMode (toRawFilePath "/dev/null") ReadOnly Nothing defaultFileFlags+ let logfd = openFdWithMode (toRawFilePath "/dev/null") ReadOnly Nothing + defaultFileFlags+ (CloseOnExecFlag True) liftIO $ daemonize git_annex ps logfd Nothing False runNonInteractive #else liftIO $ foreground Nothing runNonInteractive
Command/Sim.hs view
@@ -70,7 +70,7 @@ let st = emptySimState rng (fromOsPath simdir) case simfile of Nothing -> startup simdir st []- Just f -> liftIO (readFile f) >>= \c -> + Just f -> liftIO (readFileString (toOsPath f)) >>= \c -> case parseSimFile c of Left err -> giveup err Right cs -> startup simdir st cs
Command/Smudge.hs view
@@ -173,7 +173,7 @@ doingest preferredbackend = do -- Can't restage associated files because git add -- runs this and has the index locked.- let norestage = Restage False+ let norestage = NoRestage emitpointer =<< postingest =<< (\ld -> ingest' preferredbackend nullMeterUpdate ld Nothing norestage)@@ -294,7 +294,7 @@ obj <- calcRepo (gitAnnexLocation k) -- Cannot restage because git add is running and has -- the index locked.- populatePointerFile (Restage False) k obj file >>= \case+ populatePointerFile NoRestage k obj file >>= \case Nothing -> return () Just ic -> Database.Keys.addInodeCaches k [ic] @@ -305,7 +305,7 @@ -- Doing it explicitly here avoids a later pause in the middle of -- some other action. scanAnnexedFiles- updateSmudged (Restage True)+ updateSmudged LaterRestage stop updateSmudged :: Restage -> Annex ()
Command/Sync.hs view
@@ -606,8 +606,8 @@ Command.Import.listContents' remote ImportTree (CheckGitIgnore False) go where go (Just importable) = importChanges remote ImportTree False True importable >>= \case- ImportFinished imported -> do- (_t, updatestate) <- recordImportTree remote ImportTree Nothing imported+ ImportFinished postexportlogupdate imported -> do+ (_t, updatestate) <- recordImportTree remote ImportTree Nothing imported postexportlogupdate next $ do updatestate return True
Command/Unlock.hs view
@@ -67,6 +67,6 @@ cleanup :: OsPath -> Maybe InodeCache -> Key -> Maybe FileMode -> CommandCleanup cleanup dest destic key destmode = do stagePointerFile dest destmode =<< hashPointerFile key- maybe noop (restagePointerFile (Restage True) dest) destic+ maybe noop (restagePointerFile QueueRestage dest) destic Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath dest) return True
Command/Vicfg.hs view
@@ -49,7 +49,7 @@ createAnnexDirectory $ parentDir f cfg <- getCfg descs <- uuidDescriptions- liftIO $ writeFile (fromOsPath f) $ genCfg cfg descs+ liftIO $ writeFileString f $ genCfg cfg descs vicfg cfg f stop @@ -65,7 +65,7 @@ liftIO $ removeWhenExistsWith removeFile f case r of Left s -> do- liftIO $ writeFile (fromOsPath f) s+ liftIO $ writeFileString f s vicfg curcfg f Right newcfg -> setCfg curcfg newcfg where
Command/WebApp.hs view
@@ -90,7 +90,7 @@ ( if isJust (listenAddress o) || isJust (listenPort o) then giveup "The assistant is already running, so --listen and --port cannot be used." else do- url <- liftIO . readFile . fromOsPath+ url <- liftIO . readFileString =<< fromRepo gitAnnexUrlFile liftIO $ if isJust listenAddress' then putStrLn url
Common.hs view
@@ -33,5 +33,6 @@ import Utility.Split as X import Utility.FileSystemEncoding as X import Utility.OsPath as X+import Utility.FileIO as X (readFileString, writeFileString, appendFileString) import Utility.PartialPrelude as X
Config/Files.hs view
@@ -32,4 +32,4 @@ noAnnexFileContent :: Maybe OsPath -> IO (Maybe String) noAnnexFileContent repoworktree = case repoworktree of Nothing -> return Nothing- Just wt -> catchMaybeIO (readFile (fromOsPath (wt </> literalOsPath ".noannex")))+ Just wt -> catchMaybeIO (readFileString (wt </> literalOsPath ".noannex"))
Config/Files/AutoStart.hs view
@@ -18,7 +18,7 @@ readAutoStartFile = do f <- autoStartFile filter valid . nub . map (dropTrailingPathSeparator . toOsPath) . lines- <$> catchDefaultIO "" (readFile (fromOsPath f))+ <$> catchDefaultIO "" (readFileString f) where -- Ignore any relative paths; some old buggy versions added eg "." valid = isAbsolute@@ -30,7 +30,7 @@ when (dirs' /= dirs) $ do f <- autoStartFile createDirectoryIfMissing True (parentDir f)- viaTmp (writeFile . fromRawFilePath . fromOsPath) f+ viaTmp writeFileString f (unlines (map fromOsPath dirs')) {- Adds a directory to the autostart file. If the directory is already
Config/Smudge.hs view
@@ -70,7 +70,7 @@ lf <- Annex.fromRepo Git.attributesLocal ls <- liftIO $ catchDefaultIO [] $ map decodeBS . fileLines' <$> F.readFile' lf- liftIO $ writeFile (fromOsPath lf) $ unlines $+ liftIO $ writeFileString lf $ unlines $ filter (\l -> l `notElem` stdattr && not (null l)) ls unsetConfig (ConfigKey "filter.annex.smudge") unsetConfig (ConfigKey "filter.annex.clean")
Crypto.hs view
@@ -25,7 +25,6 @@ decryptCipher', encryptKey, isEncKey,- feedFile, feedBytes, readBytes, readBytesStrictly,@@ -187,9 +186,6 @@ type Feeder = Handle -> IO () type Reader m a = Handle -> m a--feedFile :: FilePath -> Feeder-feedFile f h = L.hPut h =<< L.readFile f feedBytes :: L.ByteString -> Feeder feedBytes = flip L.hPut
Database/Keys.hs view
@@ -47,7 +47,6 @@ import Git.FilePath import Git.Command import Git.Types-import Git.Index import Git.Sha import Git.CatFile import Git.Branch (writeTreeQuiet, update')@@ -81,8 +80,8 @@ else return tableschanged v <- a (SQL.ReadHandle qh) return (v, DbOpen (qh, tableschanged'))- go DbClosed = do- st <- openDb False DbClosed+ go startst@(DbClosed _) = do+ st <- openDb False startst v <- case st of (DbOpen (qh, _)) -> a (SQL.ReadHandle qh) _ -> return mempty@@ -124,7 +123,11 @@ openDb :: Bool -> DbState -> Annex DbState openDb _ st@(DbOpen _) = return st openDb False DbUnavailable = return DbUnavailable-openDb forwrite _ = do+openDb forwrite (DbClosed wasopen) = openDb' forwrite wasopen+openDb forwrite DbUnavailable = openDb' forwrite (DbWasOpen False) ++openDb' :: Bool -> DbWasOpen -> Annex DbState+openDb' forwrite wasopen = do lck <- calcRepo' gitAnnexKeysDbLock catchPermissionDenied permerr $ withExclusiveLock lck $ do dbdir <- calcRepo' gitAnnexKeysDbDir@@ -144,7 +147,7 @@ open db dbisnew = do qh <- liftIO $ H.openDbQueue db SQL.containedTable- tc <- reconcileStaged dbisnew qh+ tc <- reconcileStaged dbisnew qh wasopen return $ DbOpen (qh, tc) {- Closes the database if it was open. Any writes will be flushed to it.@@ -238,8 +241,8 @@ - This is run with a lock held, so only one process can be running this at - a time. -- - To avoid unnecessary work, the index file is statted, and if it's not- - changed since last time this was run, nothing is done.+ - If the database gets closed and then reopened by the same process, this+ - will avoid doing any repeated work. - - A tree is generated from the index, and the diff between that tree - and the last processed tree is examined for changes.@@ -259,30 +262,19 @@ - So when using getAssociatedFiles, have to make sure the file still - is an associated file. -}-reconcileStaged :: Bool -> H.DbQueue -> Annex DbTablesChanged-reconcileStaged dbisnew qh = ifM isBareRepo+reconcileStaged :: Bool -> H.DbQueue -> DbWasOpen -> Annex DbTablesChanged+reconcileStaged _ _ (DbWasOpen True) =+ return (DbTablesChanged False False)+reconcileStaged dbisnew qh _ = ifM isBareRepo ( return mempty- , do- gitindex <- inRepo currentIndexFile- indexcache <- fromOsPath <$> calcRepo' gitAnnexKeysDbIndexCache- withTSDelta (liftIO . genInodeCache gitindex) >>= \case- Just cur -> readindexcache indexcache >>= \case- Nothing -> go cur indexcache =<< getindextree- Just prev -> ifM (compareInodeCaches prev cur)- ( return mempty- , go cur indexcache =<< getindextree- )- Nothing -> return mempty+ , inReconcileStaged $ go =<< getindextree ) where lastindexref = Ref "refs/annex/last-index" - readindexcache indexcache = liftIO $ maybe Nothing readInodeCache- <$> catchMaybeIO (readFile indexcache)- getoldtree = fromMaybe emptyTree <$> inRepo (Git.Ref.sha lastindexref) - go cur indexcache (Just newtree) = do+ go (Just newtree) = do oldtree <- getoldtree when (oldtree /= newtree) $ do fastDebug "Database.Keys" "reconcileStaged start"@@ -292,7 +284,6 @@ (Just (fromRef oldtree)) (fromRef newtree) (procdiff mdfeeder)- liftIO $ writeFile indexcache $ showInodeCache cur -- Storing the tree in a ref makes sure it does not -- get garbage collected, and is available to diff -- against next time.@@ -309,7 +300,7 @@ -- When there is a merge conflict, that will not see the new local -- version of the files that are conflicted. So a second diff -- is done, with --staged but no old tree.- go _ _ Nothing = do+ go Nothing = do fastDebug "Database.Keys" "reconcileStaged start (in conflict)" oldtree <- getoldtree g <- Annex.gitRepo@@ -442,13 +433,19 @@ filepopulated <- sameInodeCache p ics case (keypopulated, filepopulated) of (True, False) ->- populatePointerFile (Restage True) key obj p >>= \case+ populatePointerFile restage key obj p >>= \case Nothing -> return () Just ic -> addinodecaches key (catMaybes [Just ic, mobjic])- (False, True) -> depopulatePointerFile key p+ (False, True) -> depopulatePointerFile restage key p _ -> return () + -- Cannot use QueueRestage here, because it could deadlock;+ -- restagePointerFiles tries to close the database handle,+ -- but the database handle is open while reconcileStaged + -- is running.+ restage = LaterRestage+ send :: ((Maybe Key -> Annex a, Ref) -> IO ()) -> Ref -> (Maybe Key -> Annex a) -> IO () send feeder r withk = feeder (withk, r) @@ -508,6 +505,15 @@ addassociatedfile | dbisnew = SQL.newAssociatedFile | otherwise = SQL.addAssociatedFile++-- Avoid a potential deadlock.+inReconcileStaged :: Annex a -> Annex a+inReconcileStaged = bracket setup cleanup . const+ where+ setup = Annex.changeState $ \s -> s+ { Annex.inreconcilestaged = True }+ cleanup () = Annex.changeState $ \s -> s+ { Annex.inreconcilestaged = False } {- Normally the keys database is updated incrementally when opened, - by reconcileStaged. Calling this explicitly allows running the
Database/Keys/Handle.hs view
@@ -9,6 +9,7 @@ DbHandle, newDbHandle, DbState(..),+ DbWasOpen(..), withDbState, flushDbQueue, closeDbHandle,@@ -21,8 +22,6 @@ import Control.Concurrent import Control.Monad.IO.Class (liftIO, MonadIO)-import Control.Applicative-import Prelude -- The MVar is always left full except when actions are run -- that access the database.@@ -30,10 +29,16 @@ -- The database can be closed or open, but it also may have been -- tried to open (for read) and didn't exist yet or is not readable.-data DbState = DbClosed | DbOpen (H.DbQueue, DbTablesChanged) | DbUnavailable+data DbState + = DbClosed DbWasOpen+ | DbOpen (H.DbQueue, DbTablesChanged)+ | DbUnavailable +-- Was the database previously opened by this process?+data DbWasOpen = DbWasOpen Bool+ newDbHandle :: IO DbHandle-newDbHandle = DbHandle <$> newMVar DbClosed+newDbHandle = DbHandle <$> newMVar (DbClosed (DbWasOpen False)) -- Runs an action on the state of the handle, which can change its state. -- The MVar is empty while the action runs, which blocks other users@@ -65,5 +70,5 @@ where go (DbOpen (qh, _)) = do H.closeDbQueue qh- return ((), DbClosed)+ return ((), DbClosed (DbWasOpen True)) go st = return ((), st)
Database/Keys/Tables.hs view
@@ -7,10 +7,6 @@ module Database.Keys.Tables where -import Data.Monoid-import qualified Data.Semigroup as Sem-import Prelude- data DbTable = AssociatedTable | ContentTable deriving (Eq, Show) @@ -20,7 +16,7 @@ } deriving (Show) -instance Sem.Semigroup DbTablesChanged where+instance Semigroup DbTablesChanged where a <> b = DbTablesChanged { associatedTable = associatedTable a || associatedTable b , contentTable = contentTable a || contentTable b
Database/Queue.hs view
@@ -27,8 +27,6 @@ import Database.Persist.Sqlite import Control.Concurrent import Data.Time.Clock-import Control.Applicative-import Prelude {- A DbQueue wraps a DbHandle, adding a queue of writes to perform. -
Git/Construct.hs view
@@ -280,7 +280,7 @@ adjustGitDirFile' :: RepoLocation -> IO (Maybe RepoLocation) adjustGitDirFile' loc@(Local {}) = do let gd = gitdir loc- c <- firstLine <$> catchDefaultIO "" (readFile (fromOsPath gd))+ c <- firstLine <$> catchDefaultIO "" (readFileString gd) if gitdirprefix `isPrefixOf` c then do top <- takeDirectory <$> absPath gd
Git/Fsck.hs view
@@ -26,8 +26,6 @@ import qualified Data.Set as S import Control.Concurrent.Async-import qualified Data.Semigroup as Sem-import Prelude data FsckResults = FsckFoundMissing@@ -56,7 +54,7 @@ appendFsckOutput AllDuplicateEntriesWarning NoFsckOutput = AllDuplicateEntriesWarning appendFsckOutput NoFsckOutput AllDuplicateEntriesWarning = AllDuplicateEntriesWarning -instance Sem.Semigroup FsckOutput where+instance Semigroup FsckOutput where (<>) = appendFsckOutput instance Monoid FsckOutput where
Git/Hook.hs view
@@ -59,7 +59,7 @@ f = hookFile h r go = do -- On Windows, using a ByteString as the file content- -- avoids the newline translation done by writeFile.+ -- avoids the newline translation done by writeFileString. -- Hook scripts on Windows could use CRLF endings, but -- they typically use unix newlines, which does work there -- and makes the repository more portable.@@ -85,11 +85,11 @@ expectedContent :: Hook -> Repo -> IO ExpectedContent expectedContent h r = do- -- Note that on windows, this readFile does newline translation,+ -- Note that on windows, this readFileString does newline translation, -- and so a hook file that has CRLF will be treated the same as one -- that has LF. That is intentional, since users may have a reason -- to prefer one or the other.- content <- readFile $ fromOsPath $ hookFile h r+ content <- readFileString $ hookFile h r return $ if content == hookScript h then ExpectedContent else if any (content ==) (hookOldScripts h)
Git/LockFile.hs view
@@ -53,8 +53,7 @@ #ifndef mingw32_HOST_OS -- On unix, git simply uses O_EXCL h <- openFdWithMode (fromOsPath lck) ReadWrite (Just 0O666)- (defaultFileFlags { exclusive = True })- setFdOption h CloseOnExec True+ (defaultFileFlags { exclusive = True }) (CloseOnExecFlag True) #else -- It's not entirely clear how git manages locking on Windows, -- since it's buried in the portability layer, and different
Git/Objects.hs view
@@ -50,7 +50,7 @@ listAlternates :: Repo -> IO [FilePath] listAlternates r = catchDefaultIO [] $- lines <$> readFile (fromOsPath alternatesfile)+ lines <$> readFileString alternatesfile where alternatesfile = objectsDir r </> literalOsPath "info" </> literalOsPath "alternates"
Git/Quote.hs view
@@ -28,8 +28,6 @@ import Data.Word import Data.String import qualified Data.ByteString as S-import qualified Data.Semigroup as Sem-import Prelude unquote :: S.ByteString -> RawFilePath unquote b = case S.uncons b of@@ -108,7 +106,7 @@ instance IsString StringContainingQuotedPath where fromString = UnquotedByteString . encodeBS -instance Sem.Semigroup StringContainingQuotedPath where+instance Semigroup StringContainingQuotedPath where UnquotedString a <> UnquotedString b = UnquotedString (a <> b) UnquotedByteString a <> UnquotedByteString b = UnquotedByteString (a <> b) a <> b = a :+: b
Git/Repair.hs view
@@ -269,7 +269,7 @@ let dest = gitd </> toOsPath (fromRef' ref) createDirectoryUnder [gitd] (parentDir dest) unlessM (doesFileExist dest) $- writeFile (fromOsPath dest) (fromRef sha)+ writeFileString dest (fromRef sha) packedRefsFile :: Repo -> OsPath packedRefsFile r = localGitDir r </> literalOsPath "packed-refs"@@ -472,7 +472,7 @@ preRepair g = do unlessM (validhead <$> catchDefaultIO "" (decodeBS <$> safeReadFile headfile)) $ do removeWhenExistsWith removeFile headfile- writeFile (fromOsPath headfile) "ref: refs/heads/master"+ writeFileString headfile "ref: refs/heads/master" explodePackedRefsFile g unless (repoIsLocalBare g) $ void $ tryIO $ allowWrite $ indexFile g
Git/Types.hs view
@@ -21,8 +21,6 @@ import qualified Data.ByteString as S import qualified Data.List.NonEmpty as NE import System.Posix.Types-import qualified Data.Semigroup as Sem-import Prelude {- Support repositories on local disk, and repositories accessed via an URL. -@@ -81,7 +79,7 @@ -- with an empty value deriving (Ord, Eq) -instance Sem.Semigroup ConfigValue where+instance Semigroup ConfigValue where ConfigValue a <> ConfigValue b = ConfigValue (a <> b) a <> NoConfigValue = a NoConfigValue <> b = b
Logs/File.hs view
@@ -38,7 +38,7 @@ writeLogFile f c = createDirWhenNeeded f $ viaTmp writelog f c where writelog tmp c' = do- liftIO $ writeFile (fromOsPath tmp) c'+ liftIO $ writeFileString tmp c' setAnnexFilePerm tmp -- | Runs the action with a handle connected to a temp file.
Logs/FsckResults.hs view
@@ -45,7 +45,7 @@ readFsckResults u = do logfile <- fromRepo $ gitAnnexFsckResultsLog u liftIO $ catchDefaultIO (FsckFoundMissing S.empty False) $- deserializeFsckResults <$> readFile (fromOsPath logfile)+ deserializeFsckResults <$> readFileString logfile deserializeFsckResults :: String -> FsckResults deserializeFsckResults = deserialize . lines
Logs/Import.hs view
@@ -1,6 +1,6 @@ {- git-annex import logs -- - Copyright 2023 Joey Hess <id@joeyh.name>+ - Copyright 2023-2025 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -14,24 +14,60 @@ import Git.Types import Git.Sha import Logs.File+import Logs.Export import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Char8 as S8+import qualified Data.Set as S {- Records the sha of a tree that contains hashes of ContentIdentifiers- - that were imported from a remote. -}+ - that were imported from a remote.+ -+ - The sha is on the first line of the log file, and following it+ - is a line with the the currently exported treeishs, and then a line with+ - the incomplete exported treeishes.+ -} recordContentIdentifierTree :: UUID -> Sha -> Annex () recordContentIdentifierTree u t = do l <- calcRepo' (gitAnnexImportLog u)- writeLogFile l (fromRef t)+ exported <- getExport u+ writeLogFile l $ unlines+ [ fromRef t+ , unwords $ map fromRef $ exportedTreeishes exported+ , unwords $ map fromRef $ incompleteExportedTreeishes exported+ ] -{- Gets the tree last recorded for a remote. -}+{- Gets the ContentIdentifier tree last recorded for a remote.+ -+ - This returns Nothing if no tree was recorded yet. + -+ - It also returns Nothing when there have been changes to what is exported+ - to the remote since the tree was recorded. That avoids a problem where+ - diffing from the current Contentidentifier tree to the previous tree+ - would miss changes that were made to a remote by an export, but were+ - later undone manually. For example, if a file was exported to the remote,+ - and then the file was manually removed from the remote, the current tree+ - would not contain the file, and neither would the previous tree.+ - So diffing between the trees would miss that removal. The removed+ - file would then remain in the imported tree.+ -} getContentIdentifierTree :: UUID -> Annex (Maybe Sha) getContentIdentifierTree u = do l <- calcRepo' (gitAnnexImportLog u) -- This is safe because the log file is written atomically.- calcLogFileUnsafe l Nothing update+ ls <- calcLogFileUnsafe l [] (\v ls -> L.toStrict v : ls)+ exported <- getExport u+ return $ case reverse ls of+ -- Subsequent lines are ignored. This leaves room for future+ -- expansion of what is logged.+ (a:b:c:_) -> do+ t <- extractSha a+ exportedtreeishs <- mapM extractSha (S8.words b)+ incompleteexportedtreeishs <- mapM extractSha (S8.words c)+ if same exportedtreeishs (exportedTreeishes exported) && + same incompleteexportedtreeishs (incompleteExportedTreeishes exported)+ then Just t+ else Nothing+ _ -> Nothing where- update l Nothing = extractSha (L.toStrict l)- -- Subsequent lines are ignored. This leaves room for future- -- expansion of what is logged.- update _l (Just l) = Just l+ same l1 l2 = S.fromList l1 == S.fromList l2
Logs/MapLog.hs view
@@ -28,8 +28,6 @@ import qualified Data.Attoparsec.ByteString.Lazy as AL import qualified Data.Attoparsec.ByteString.Char8 as A8 import Data.ByteString.Builder-import qualified Data.Semigroup as Sem-import Prelude data LogEntry v = LogEntry { changed :: VectorClock@@ -42,7 +40,7 @@ newtype MapLog f v = MapLog (M.Map f (LogEntry v)) deriving (Show, Eq) -instance Ord f => Sem.Semigroup (MapLog f v)+instance Ord f => Semigroup (MapLog f v) where a <> MapLog b = foldl' (\m (f, v) -> addMapLog f v m) a (M.toList b)
Logs/Restage.hs view
@@ -55,7 +55,7 @@ ifM (doesPathExist oldf) ( do h <- F.openFile oldf AppendMode- hPutStr h =<< readFile (fromOsPath logf)+ hPutStr h =<< readFileString logf hClose h liftIO $ removeWhenExistsWith removeFile logf , moveFile logf oldf
Logs/Schedule.hs view
@@ -63,9 +63,9 @@ getLastRunTimes :: Annex (M.Map ScheduledActivity LocalTime) getLastRunTimes = do- f <- fromOsPath <$> fromRepo gitAnnexScheduleState+ f <- fromRepo gitAnnexScheduleState liftIO $ fromMaybe M.empty- <$> catchDefaultIO Nothing (readish <$> readFile f)+ <$> catchDefaultIO Nothing (readish <$> readFileString f) setLastRunTime :: ScheduledActivity -> LocalTime -> Annex () setLastRunTime activity lastrun = do
Logs/Transfer.hs view
@@ -263,7 +263,7 @@ -- after it's been created with the right perms by writeTransferInfoFile. updateTransferInfoFile :: TransferInfo -> OsPath -> IO () updateTransferInfoFile info tfile = - writeFile (fromOsPath tfile) $ writeTransferInfo info+ writeFileString tfile $ writeTransferInfo info {- File format is a header line containing the startedTime and any - bytesComplete value. Followed by a newline and the associatedFile.
Logs/View.hs view
@@ -54,8 +54,8 @@ recentViews :: Annex [View] recentViews = do- f <- fromOsPath <$> fromRepo gitAnnexViewLog- liftIO $ mapMaybe readish . lines <$> catchDefaultIO [] (readFile f)+ f <- fromRepo gitAnnexViewLog+ liftIO $ mapMaybe readish . lines <$> catchDefaultIO [] (readFileString f) {- Gets the currently checked out view, if there is one. -
Messages/JSON.hs view
@@ -31,7 +31,6 @@ module Utility.Aeson, ) where -import Control.Applicative import qualified Data.Map as M import qualified Data.Vector as V import qualified Data.ByteString as S@@ -41,8 +40,6 @@ import System.IO.Unsafe (unsafePerformIO) import Control.Concurrent import Data.Maybe-import Data.Monoid-import Prelude import Types.Command (SeekInput(..)) import Types.ActionItem
P2P/Http/Server.hs view
@@ -477,14 +477,19 @@ let lock = do lockresv <- newEmptyTMVarIO unlockv <- newEmptyTMVarIO+ -- A single worker thread takes the lock, and keeps running+ -- until unlock in order to keep the lock held. annexworker <- async $ inAnnexWorker st $ do lockres <- runFullProto (clientRunState conn) (clientP2PConnection conn) $ do net $ sendMessage (LOCKCONTENT k) checkSuccess liftIO $ atomically $ putTMVar lockresv lockres- liftIO $ atomically $ takeTMVar unlockv- void $ runFullProto (clientRunState conn) (clientP2PConnection conn) $ do- net $ sendMessage UNLOCKCONTENT+ case lockres of+ Right True -> do+ liftIO $ atomically $ takeTMVar unlockv+ void $ runFullProto (clientRunState conn) (clientP2PConnection conn) $ do+ net $ sendMessage UNLOCKCONTENT+ _ -> return () atomically (takeTMVar lockresv) >>= \case Right True -> return (Just (annexworker, unlockv)) _ -> return Nothing
P2P/Http/State.hs view
@@ -44,8 +44,6 @@ import qualified Data.Set as S import Control.Concurrent.Async import Data.Time.Clock.POSIX-import qualified Data.Semigroup as Sem-import Prelude data P2PHttpServerState = P2PHttpServerState { servedRepos :: M.Map UUID PerRepoServerState@@ -62,7 +60,7 @@ , updateRepos = const mempty } -instance Sem.Semigroup P2PHttpServerState where+instance Semigroup P2PHttpServerState where a <> b = P2PHttpServerState { servedRepos = servedRepos a <> servedRepos b , serverShutdownCleanup = do
P2P/Protocol.hs view
@@ -44,9 +44,7 @@ import Data.Char import Data.Maybe import Data.Time.Clock.POSIX-import Control.Applicative import Control.DeepSeq-import Prelude newtype Offset = Offset Integer deriving (Show, Eq, NFData, Num, Real, Ord, Enum, Integral)
Remote/Directory.hs view
@@ -470,10 +470,12 @@ docopynoncow iv = do #ifndef mingw32_HOST_OS- let open = do+ let open = noCreateProcessWhile $ do+ fd <- openFdWithMode f' ReadOnly Nothing+ defaultFileFlags (CloseOnExecFlag True) -- Need a duplicate fd for the post check.- fd <- openFdWithMode f' ReadOnly Nothing defaultFileFlags dupfd <- dup fd+ setFdOption dupfd CloseOnExec True h <- fdToHandle fd return (h, dupfd) let close (h, dupfd) = do
Remote/Directory/LegacyChunked.hs view
@@ -33,7 +33,7 @@ let chunkcount = f ++ Legacy.chunkCount ifM (check chunkcount) ( do- chunks <- Legacy.listChunks f <$> readFile chunkcount+ chunks <- Legacy.listChunks f <$> readFileString (toOsPath chunkcount) ifM (allM check chunks) ( a chunks , return False ) , do@@ -52,14 +52,14 @@ storeLegacyChunked meterupdate chunksize alldests@(firstdest:_) b | L.null b = do -- always write at least one file, even for empty- L.writeFile firstdest b+ F.writeFile (toOsPath firstdest) b return [firstdest] | otherwise = storeLegacyChunked' meterupdate chunksize alldests (L.toChunks b) [] storeLegacyChunked' :: MeterUpdate -> ChunkSize -> [FilePath] -> [S.ByteString] -> [FilePath] -> IO [FilePath] storeLegacyChunked' _ _ [] _ _ = error "ran out of dests" storeLegacyChunked' _ _ _ [] c = return $ reverse c storeLegacyChunked' meterupdate chunksize (d:dests) bs c = do- bs' <- withFile d WriteMode $+ bs' <- F.withFile (toOsPath d) WriteMode $ feed zeroBytesProcessed chunksize bs storeLegacyChunked' meterupdate chunksize dests bs' (d:c) where@@ -85,7 +85,7 @@ recorder f s = do let f' = toOsPath f void $ tryIO $ allowWrite f'- writeFile f s+ writeFileString f' s void $ tryIO $ preventWrite f' store :: FilePath -> ChunkSize -> (OsPath -> OsPath -> IO ()) -> Key -> L.ByteString -> MeterUpdate -> FilePath -> FilePath -> IO ()@@ -103,7 +103,7 @@ let go = \k sink -> do liftIO $ void $ withStoredFiles (fromOsPath d) (legacyLocations locations) k $ \fs -> do forM_ fs $- F.appendFile' tmp <=< S.readFile+ F.appendFile' tmp <=< F.readFile' . toOsPath return True b <- liftIO $ F.readFile tmp liftIO $ removeWhenExistsWith removeFile tmp
Remote/Helper/Chunked/Legacy.hs view
@@ -10,6 +10,7 @@ import Annex.Common import Remote.Helper.Chunked import Utility.Metered+import qualified Utility.FileIO as F import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L@@ -116,6 +117,6 @@ -} meteredWriteFileChunks :: MeterUpdate -> FilePath -> [v] -> (v -> IO L.ByteString) -> IO () meteredWriteFileChunks meterupdate dest chunks feeder =- withBinaryFile dest WriteMode $ \h ->+ F.withBinaryFile (toOsPath dest) WriteMode $ \h -> forM_ chunks $ meteredWrite meterupdate (S.hPut h) <=< feeder
Remote/Helper/Special.hs view
@@ -96,7 +96,7 @@ fileStorer :: (Key -> OsPath -> MeterUpdate -> Annex ()) -> Storer fileStorer a k (FileContent f) m = a k f m fileStorer a k (ByteContent b) m = withTmp k $ \f -> do- liftIO $ L.writeFile (fromOsPath f) b+ liftIO $ F.writeFile f b a k f m -- A Storer that expects to be provided with a L.ByteString of
Remote/Tahoe.hs view
@@ -213,7 +213,7 @@ writeSharedConvergenceSecret :: TahoeConfigDir -> SharedConvergenceSecret -> IO () writeSharedConvergenceSecret configdir scs = - writeFile (fromOsPath (convergenceFile configdir))+ writeFileString (convergenceFile configdir) (unlines [scs]) {- The tahoe daemon writes the convergenceFile shortly after it starts@@ -223,11 +223,11 @@ getSharedConvergenceSecret :: TahoeConfigDir -> IO SharedConvergenceSecret getSharedConvergenceSecret configdir = go (60 :: Int) where- f = fromOsPath $ convergenceFile configdir+ f = convergenceFile configdir go n- | n == 0 = giveup $ "tahoe did not write " ++ f ++ " after 1 minute. Perhaps the daemon failed to start?"+ | n == 0 = giveup $ "tahoe did not write " ++ fromOsPath f ++ " after 1 minute. Perhaps the daemon failed to start?" | otherwise = do- v <- catchMaybeIO (readFile f)+ v <- catchMaybeIO (readFileString f) case v of Just s | "\n" `isSuffixOf` s || "\r" `isSuffixOf` s -> return $ takeWhile (`notElem` ("\n\r" :: String)) s
Test.hs view
@@ -38,8 +38,8 @@ import qualified Git.Ref import qualified Git.LsTree import qualified Git.FilePath-import qualified Annex.Locations #ifndef mingw32_HOST_OS+import qualified Annex.Locations import qualified Git.Bundle import qualified Types.GitConfig #endif@@ -556,8 +556,8 @@ #ifdef WITH_MAGICMIME git "config" ["annex.largefiles", "mimeencoding=binary"] "git config annex.largefiles"- writeFile "binary" "\127"- writeFile "text" "test\n" + writeFileString (literalOsPath "binary") "\127"+ writeFileString (literalOsPath "text") "test\n" git_annex "add" ["binary", "text"] "git-annex add with mimeencoding in largefiles" git_annex "sync" ["--no-content"]@@ -879,7 +879,7 @@ changecontent annexedfile git "add" [annexedfile] "add of modified file" runchecks [checkregularfile, checkwritable] annexedfile- c <- readFile annexedfile+ c <- readFileString (toOsPath annexedfile) assertEqual "content of modified file" c (changedcontent annexedfile) git_annex_shouldfail "drop" [annexedfile] "drop with no known copy of modified file should not be allowed"@@ -896,8 +896,6 @@ Just k <- Annex.WorkTree.lookupKey (toOsPath annexedfile) Database.Keys.removeInodeCaches k Database.Keys.closeDb- liftIO . removeWhenExistsWith removeFile- =<< Annex.calcRepo' Annex.Locations.gitAnnexKeysDbIndexCache writecontent annexedfile "test_lock_force content" git_annex_shouldfail "lock" [annexedfile] "lock of modified file should not be allowed" git_annex "lock" ["--force", annexedfile] "lock --force of modified file"@@ -921,7 +919,7 @@ then git_annex "pre-commit" [] "pre-commit" else git "commit" ["-q", "-m", "contentchanged"] "git commit of edited file" runchecks [checkregularfile, checkwritable] annexedfile- c <- readFile annexedfile+ c <- readFileString (toOsPath annexedfile) assertEqual "content of modified file" c (changedcontent annexedfile) git_annex_shouldfail "drop" [annexedfile] "drop no known copy of modified file should not be allowed" @@ -947,7 +945,7 @@ git "mv" [annexedfile, subdir] "git mv" git_annex "fix" [newfile] "fix of moved file" runchecks [checklink, checkunwritable] newfile- c <- readFile newfile+ c <- readFileString (toOsPath newfile) assertEqual "content of moved file" c (content annexedfile) where subdir = "s"@@ -1069,7 +1067,8 @@ annexed_present sha1annexedfile if usegitattributes then do- writeFile ".gitattributes" "* annex.backend=SHA1"+ writeFileString (literalOsPath ".gitattributes")+ "* annex.backend=SHA1" git_annex "migrate" [sha1annexedfile] "migrate sha1annexedfile" git_annex "migrate" [annexedfile]@@ -1085,7 +1084,8 @@ checkbackend sha1annexedfile backendSHA1 -- check that reversing a migration works- writeFile ".gitattributes" "* annex.backend=SHA256"+ writeFileString (literalOsPath ".gitattributes")+ "* annex.backend=SHA256" git_annex "migrate" [sha1annexedfile] "migrate sha1annexedfile" git_annex "migrate" [annexedfile] "migrate annexedfile" annexed_present annexedfile@@ -1531,7 +1531,7 @@ length v == 1 @? (what ++ " too many variant files in: " ++ show v) conflictor `elem` l @? (what ++ " conflictor file missing in: " ++ show l)- s <- catchMaybeIO $ readFile $ fromOsPath $+ s <- catchMaybeIO $ readFileString $ toOsPath d </> toOsPath conflictor s == Just nonannexed_content @? (what ++ " wrong content for nonannexed file: " ++ show s)@@ -1918,19 +1918,19 @@ where testscheme scheme = intmpclonerepo $ test_with_gpg $ \gpgcmd environ -> do createDirectory (literalOsPath "dir")- let initps =+ let ps = [ "foo"- , "type=directory" , "encryption=" ++ scheme , "directory=dir" , "highRandomQuality=false" ] ++ if scheme `elem` ["hybrid","pubkey"] then ["keyid=" ++ Utility.Gpg.testKeyId] else []+ let initps = ps ++ [ "type=directory" ] 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' "enableremote" ps (Just environ) "enableremote"+ git_annex' "enableremote" ps (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"] (Just environ) "copy --to encrypted remote"@@ -2074,9 +2074,9 @@ dircontains "import" (content "newimport3") where dircontains f v = do- let df = fromOsPath (literalOsPath "dir" </> stringToOsPath f)- ((v==) <$> readFile df)- @? ("did not find expected content of " ++ df)+ let df = literalOsPath "dir" </> stringToOsPath f+ ((v==) <$> readFileString df)+ @? ("did not find expected content of " ++ fromOsPath df) writedir f = writecontent (fromOsPath (literalOsPath "dir" </> stringToOsPath f)) -- When on an adjusted branch, this updates the master branch -- to match it, which is necessary since the master branch is going@@ -2111,9 +2111,9 @@ testexport where dircontains f v = do- let df = fromOsPath (literalOsPath "dir" </> toOsPath f)- ((v==) <$> readFile df)- @? ("did not find expected content of " ++ df)+ let df = literalOsPath "dir" </> toOsPath f+ ((v==) <$> readFileString df)+ @? ("did not find expected content of " ++ fromOsPath df) subdir = "subdir" subannexedfile = fromOsPath $
Test/Framework.hs view
@@ -391,7 +391,7 @@ checkcontent :: FilePath -> Assertion checkcontent f = do- c <- Utility.Exception.catchDefaultIO "could not read file" $ readFile f+ c <- Utility.Exception.catchDefaultIO "could not read file" $ readFileString (toOsPath f) assertEqual ("checkcontent " ++ f) (content f) c checkunwritable :: FilePath -> Assertion@@ -415,7 +415,7 @@ checkdangling f = ifM (annexeval Config.crippledFileSystem) ( return () -- probably no real symlinks to test , do- r <- tryIO $ readFile f+ r <- tryIO $ readFileString (toOsPath f) case r of Left _ -> return () -- expected; dangling link Right _ -> assertFailure $ f ++ " was not a dangling link as expected"@@ -675,9 +675,10 @@ writecontent f c = go (10000000 :: Integer) where go ticsleft = do- oldmtime <- catchMaybeIO $ getModificationTime (toOsPath f)- writeFile f c- newmtime <- getModificationTime (toOsPath f)+ let f' = toOsPath f+ oldmtime <- catchMaybeIO $ getModificationTime f'+ writeFileString f' c+ newmtime <- getModificationTime f' if Just newmtime == oldmtime then do threadDelay 100000
Types/CleanupActions.hs view
@@ -10,7 +10,7 @@ import Types.UUID import Utility.Url -import System.Process (Pid)+import Utility.Process (Pid) data CleanupAction = RemoteCleanup UUID@@ -20,6 +20,7 @@ | AdjustedBranchUpdate | TorrentCleanup URLString | OtherTmpCleanup+ | RestagePointerFiles deriving (Eq, Ord) data SignalAction
Types/DeferredParse.hs view
@@ -12,8 +12,6 @@ import Annex import Options.Applicative-import qualified Data.Semigroup as Sem-import Prelude -- Some values cannot be fully parsed without performing an action. -- The action may be expensive, so it's best to call finishParse on such a@@ -47,7 +45,7 @@ , annexReadSetter :: AnnexRead -> AnnexRead } -instance Sem.Semigroup AnnexSetter where+instance Semigroup AnnexSetter where a <> b = AnnexSetter { annexStateSetter = annexStateSetter a >> annexStateSetter b , annexReadSetter = annexReadSetter b . annexReadSetter a
Types/DesktopNotify.hs view
@@ -7,17 +7,13 @@ module Types.DesktopNotify where -import Data.Monoid-import qualified Data.Semigroup as Sem-import Prelude- data DesktopNotify = DesktopNotify { notifyStart :: Bool , notifyFinish :: Bool } deriving (Show) -instance Sem.Semigroup DesktopNotify where+instance Semigroup DesktopNotify where (DesktopNotify s1 f1) <> (DesktopNotify s2 f2) = DesktopNotify (s1 || s2) (f1 || f2)
Types/Difference.hs view
@@ -26,11 +26,8 @@ import Git.Types import Data.Maybe-import Data.Monoid import qualified Data.ByteString as B import qualified Data.Set as S-import qualified Data.Semigroup as Sem-import Prelude -- Describes differences from the standard repository format. --@@ -85,7 +82,7 @@ } appendDifferences _ _ = UnknownDifferences -instance Sem.Semigroup Differences where+instance Semigroup Differences where (<>) = appendDifferences instance Monoid Differences where
Types/Distribution.hs view
@@ -14,9 +14,6 @@ import Data.Time.Clock import Git.Config (isTrueFalse, boolConfig) -import Control.Applicative-import Prelude- type GitAnnexVersion = String data GitAnnexDistribution = GitAnnexDistribution
Types/GitConfig.hs view
@@ -157,6 +157,7 @@ , annexSkipUnknown :: Bool , annexAdjustedBranchRefresh :: Integer , annexSupportUnlocked :: Bool+ , annexAssistantAllowUnlocked :: Bool , coreSymlinks :: Bool , coreSharedRepository :: SharedRepository , coreQuotePath :: QuotePath@@ -281,6 +282,7 @@ (if getbool "adjustedbranchrefresh" False then 1 else 0) (getmayberead (annexConfig "adjustedbranchrefresh")) , annexSupportUnlocked = getbool (annexConfig "supportunlocked") True+ , annexAssistantAllowUnlocked = getbool (annexConfig "assistant.allowunlocked") False , coreSymlinks = getbool "core.symlinks" True , coreSharedRepository = getSharedRepository r , coreQuotePath = QuotePath (getbool "core.quotepath" True)
Types/GitRemoteAnnex.hs view
@@ -14,7 +14,6 @@ import Types.Key -import qualified Data.Semigroup as Sem import qualified Data.Set as S -- The manifest contains an ordered list of git bundle keys.@@ -39,7 +38,7 @@ instance Monoid Manifest where mempty = Manifest mempty mempty -instance Sem.Semigroup Manifest where+instance Semigroup Manifest where a <> b = mkManifest (inManifest a <> inManifest b) (S.union (outManifest a) (outManifest b))
Types/Key.hs view
@@ -42,11 +42,9 @@ import Data.Char import System.Posix.Types import Foreign.C.Types-import Data.Monoid import Control.Applicative import GHC.Generics import Control.DeepSeq-import Prelude {- A Key has a unique name, which is derived from a particular backend, - and may contain other optional metadata. -}
Types/Transfer.hs view
@@ -23,8 +23,6 @@ import Data.Time.Clock.POSIX import Control.Concurrent-import Control.Applicative-import Prelude {- Enough information to uniquely identify a transfer. -} data Transfer = Transfer
Types/UUID.hs view
@@ -20,7 +20,6 @@ import Data.String import Data.ByteString.Builder import Control.DeepSeq-import qualified Data.Semigroup as Sem import Common import Git.Types (ConfigValue(..))@@ -115,7 +114,7 @@ -- A description of a UUID. newtype UUIDDesc = UUIDDesc B.ByteString- deriving (Eq, Sem.Semigroup, Monoid, IsString)+ deriving (Eq, Semigroup, Monoid, IsString) fromUUIDDesc :: UUIDDesc -> String fromUUIDDesc (UUIDDesc d) = decodeBS d
Types/VectorClock.hs view
@@ -8,8 +8,6 @@ module Types.VectorClock where import Data.Time.Clock.POSIX-import Control.Applicative-import Prelude import Utility.QuickCheck
Upgrade/V2.hs view
@@ -87,7 +87,7 @@ inject :: OsPath -> OsPath -> Annex () inject source dest = do old <- fromRepo olddir- new <- liftIO (readFile $ fromOsPath $ old </> source)+ new <- liftIO $ readFileString (old </> source) Annex.Branch.change (Annex.Branch.RegardingUUID []) dest $ \prev -> encodeBL $ unlines $ nub $ lines (decodeBL prev) ++ lines new @@ -141,7 +141,7 @@ whenM (doesFileExist attributes) $ do c <- map decodeBS . fileLines' <$> F.readFile' attributes- liftIO $ viaTmp (writeFile . fromOsPath) attributes + liftIO $ viaTmp writeFileString attributes (unlines $ filter (`notElem` attrLines) c) Git.Command.run [Param "add", File (fromOsPath attributes)] repo
Upgrade/V7.hs view
@@ -136,7 +136,7 @@ <$> catchDefaultIO "" (F.readFile' lf) let ls' = removedotfilter ls when (ls /= ls') $- liftIO $ writeFile (fromOsPath lf) (unlines ls')+ liftIO $ writeFileString lf (unlines ls') where removedotfilter ("* filter=annex":".* !filter":rest) = "* filter=annex" : removedotfilter rest
Utility/Aeson.hs view
@@ -30,7 +30,6 @@ import qualified Data.Set import qualified Data.Map import qualified Data.Vector-import Prelude import Utility.FileSystemEncoding #ifdef WITH_OSPATH
Utility/Daemon.hs view
@@ -52,7 +52,8 @@ maybe noop lockPidFile pidfile a _ -> do- nullfd <- openFdWithMode (toRawFilePath "/dev/null") ReadOnly Nothing defaultFileFlags+ nullfd <- openFdWithMode (toRawFilePath "/dev/null") ReadOnly Nothing defaultFileFlags + (CloseOnExecFlag True) redir nullfd stdInput redirLog =<< openlogfd environ <- getEnvironment@@ -91,7 +92,8 @@ #endif {- Locks the pid file, with an exclusive, non-blocking lock,- - and leaves it locked on return.+ - and leaves it locked on return. The lock file is not closed on exec, so+ - when daemonize runs the process again, it inherits it. - - Writes the pid to the file, fully atomically. - Fails if the pid file is already locked by another process. -}@@ -99,9 +101,11 @@ lockPidFile pidfile = do #ifndef mingw32_HOST_OS fd <- openFdWithMode (fromOsPath pidfile) ReadWrite (Just stdFileMode) defaultFileFlags+ (CloseOnExecFlag False) locked <- catchMaybeIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)- fd' <- openFdWithMode (fromOsPath newfile) ReadWrite (Just stdFileMode) defaultFileFlags- { trunc = True }+ fd' <- openFdWithMode (fromOsPath newfile) ReadWrite (Just stdFileMode)+ (defaultFileFlags { trunc = True })+ (CloseOnExecFlag True) locked' <- catchMaybeIO $ setLock fd' (WriteLock, AbsoluteSeek, 0, 0) case (locked, locked') of (Nothing, _) -> alreadyRunning@@ -117,9 +121,9 @@ unlessM (isNothing <$> checkDaemon pidfile) alreadyRunning pid <- getPID- writeFile (fromOsPath pidfile) (show pid)+ writeFileString pidfile (show pid) lckfile <- winLockFile pid pidfile- writeFile (fromOsPath lckfile) ""+ writeFileString lckfile "" void $ lockExclusive lckfile #endif @@ -135,12 +139,15 @@ checkDaemon pidfile = bracket setup cleanup go where setup = catchMaybeIO $- openFdWithMode (fromOsPath pidfile) ReadOnly (Just stdFileMode) defaultFileFlags+ openFdWithMode (fromOsPath pidfile) ReadOnly+ (Just stdFileMode) + defaultFileFlags+ (CloseOnExecFlag True) cleanup (Just fd) = closeFd fd cleanup Nothing = return () go (Just fd) = catchDefaultIO Nothing $ do locked <- getLock fd (ReadLock, AbsoluteSeek, 0, 0)- p <- readish <$> readFile (fromOsPath pidfile)+ p <- readish <$> readFileString pidfile return (check locked p) go Nothing = return Nothing @@ -154,7 +161,7 @@ "; expected " ++ show pid ++ " )" #else checkDaemon pidfile = maybe (return Nothing) (check . readish)- =<< catchMaybeIO (readFile (fromOsPath pidfile))+ =<< catchMaybeIO (readFileString pidfile) where check Nothing = return Nothing check (Just pid) = do
Utility/Debug.hs view
@@ -23,8 +23,6 @@ import Data.String import Data.Time import System.IO.Unsafe (unsafePerformIO)-import qualified Data.Semigroup as Sem-import Prelude import Utility.FileSystemEncoding @@ -41,7 +39,7 @@ = DebugSelector (DebugSource -> Bool) | NoDebugSelector -instance Sem.Semigroup DebugSelector where+instance Semigroup DebugSelector where DebugSelector a <> DebugSelector b = DebugSelector (\v -> a v || b v) NoDebugSelector <> NoDebugSelector = NoDebugSelector NoDebugSelector <> b = b
Utility/DirWatcher/Kqueue.hs view
@@ -111,7 +111,9 @@ Nothing -> walk c rest Just info -> do mfd <- catchMaybeIO $- openFdWithMode (toRawFilePath dir) Posix.ReadOnly Nothing Posix.defaultFileFlags+ openFdWithMode (toRawFilePath dir) Posix.ReadOnly Nothing+ Posix.defaultFileFlags+ (CloseOnExecFlag True) case mfd of Nothing -> walk c rest Just fd -> do
Utility/Directory.hs view
@@ -19,10 +19,7 @@ #endif import Control.Monad import System.PosixCompat.Files (isDirectory, isSymbolicLink)-import Control.Applicative import System.IO.Unsafe (unsafeInterleaveIO)-import Data.Maybe-import Prelude import Utility.OsPath import Utility.Exception
Utility/Directory/Create.hs view
@@ -15,12 +15,9 @@ ) where import Control.Monad-import Control.Applicative import Control.Monad.IO.Class import Control.Monad.IfElse import System.IO.Error-import Data.Maybe-import Prelude import Utility.SystemDirectory import Utility.Path.AbsRel
Utility/Directory/Stream.hs view
@@ -21,7 +21,6 @@ import Control.Monad import Control.Concurrent import Data.Maybe-import Prelude #ifdef mingw32_HOST_OS import qualified System.Win32 as Win32
Utility/Env.hs view
@@ -21,7 +21,6 @@ import Utility.Exception import Control.Applicative import Data.Maybe-import Prelude import qualified System.Environment as E #else import qualified System.Posix.Env as PE
Utility/Env/Basic.hs view
@@ -13,9 +13,7 @@ ) where import Utility.Exception-import Control.Applicative import Data.Maybe-import Prelude import qualified System.Environment as E getEnv :: String -> IO (Maybe String)
Utility/FileIO.hs view
@@ -1,5 +1,14 @@-{- File IO on OsPaths.+{- This is a subset of the functions provided by file-io, supplimented with+ - readFileString, writeFileString, and appendFileString. -+ - When building with file-io, all exported functions set the close-on-exec+ - flag. Also, some other issues are handled that file-io does not handle+ - correctly.+ -+ - When not building with file-io, this provides equvilant+ - RawFilePath versions. Note that those versions do not currently+ - set the close-on-exec flag.+ - - Since Prelude exports many of these as well, this needs to be imported - qualified. -@@ -25,12 +34,17 @@ appendFile, appendFile', openTempFile,++ readFileString,+ writeFileString,+ appendFileString, ) where #ifdef WITH_OSPATH #ifndef mingw32_HOST_OS-import System.File.OsPath+import Utility.FileIO.CloseOnExec+import Utility.FileIO.String #else -- On Windows, System.File.OsPath does not handle UNC-style conversion itself, -- so that has to be done when calling it. See @@ -38,8 +52,9 @@ import Utility.Path.Windows import Utility.OsPath import System.IO (IO, Handle, IOMode)-import Prelude (return)-import qualified System.File.OsPath as O+import Prelude (String, return)+import qualified Utility.FileIO.CloseOnExec as O+import qualified Utility.FileIO.String as O import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L import Control.Applicative@@ -101,15 +116,30 @@ -- Avoid returning mangled path from convertToWindowsNativeNamespace let t' = p </> takeFileName t return (t', h)++readFileString :: OsPath -> IO String+readFileString p = do+ p' <- toOsPath <$> convertToWindowsNativeNamespace (fromOsPath p)+ O.readFileString p'++writeFileString :: OsPath -> String -> IO ()+writeFileString f txt = do+ f' <- toOsPath <$> convertToWindowsNativeNamespace (fromOsPath f)+ O.writeFileString f' txt++appendFileString :: OsPath -> String -> IO ()+appendFileString f txt = do+ f' <- toOsPath <$> convertToWindowsNativeNamespace (fromOsPath f)+ O.appendFileString f' txt #endif #else--- When not building with OsPath, export RawFilePath versions--- instead.+-- RawFilePath versions import Utility.OsPath import Utility.FileSystemEncoding import System.IO (IO, Handle, IOMode)-import Prelude ((.), return)+import Prelude (String, (.), return)+import qualified Prelude as P import qualified System.IO import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L@@ -150,4 +180,13 @@ (fromRawFilePath p) (fromRawFilePath s) return (toRawFilePath t, h)++readFileString :: OsPath -> IO String+readFileString = P.readFile . fromRawFilePath++writeFileString :: OsPath -> String -> IO ()+writeFileString = P.writeFile . fromRawFilePath++appendFileString :: OsPath -> String -> IO ()+appendFileString = P.appendFile . fromRawFilePath #endif
+ Utility/FileIO/CloseOnExec.hs view
@@ -0,0 +1,148 @@+{- This is a subset of the functions provided by file-io.+ -+ - All functions have been modified to set the close-on-exec+ - flag to True.+ -+ - Also, functions that return a Handle have been modified to+ - use the locale encoding, working around this bug:+ - https://github.com/haskell/file-io/issues/45+ -+ - Copyright 2025 Joey Hess <id@joeyh.name>+ - Copyright 2024 Julian Ospald+ -+ - License: BSD-3-clause+ -}++{-# OPTIONS_GHC -fno-warn-tabs #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Utility.FileIO.CloseOnExec+(+#ifdef WITH_OSPATH+ withFile,+ withFile',+ openFile,+ withBinaryFile,+ openBinaryFile,+ readFile,+ readFile',+ writeFile,+ writeFile',+ appendFile,+ appendFile',+ openTempFile,+#endif+) where++#ifdef WITH_OSPATH++import System.File.OsPath.Internal (withOpenFile', augmentError)+import qualified System.File.OsPath.Internal as I+import System.IO (IO, Handle, IOMode(..), hSetEncoding)+import GHC.IO.Encoding (getLocaleEncoding)+import System.OsPath (OsPath, OsString)+import Prelude (Bool(..), pure, either, (.), (>>=), ($))+import Control.Exception+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+#ifndef mingw32_HOST_OS+import System.Posix.IO+import Utility.Process+#endif++closeOnExec :: Bool+closeOnExec = True++withFile :: OsPath -> IOMode -> (Handle -> IO r) -> IO r+withFile osfp iomode act = (augmentError "withFile" osfp+ $ withOpenFileEncoding osfp iomode False False closeOnExec (try . act) True)+ >>= either ioError pure++withFile' :: OsPath -> IOMode -> (Handle -> IO r) -> IO r+withFile' osfp iomode act = (augmentError "withFile'" osfp+ $ withOpenFileEncoding osfp iomode False False closeOnExec (try . act) False)+ >>= either ioError pure++openFile :: OsPath -> IOMode -> IO Handle+openFile osfp iomode = augmentError "openFile" osfp $+ withOpenFileEncoding osfp iomode False False closeOnExec pure False++withBinaryFile :: OsPath -> IOMode -> (Handle -> IO r) -> IO r+withBinaryFile osfp iomode act = (augmentError "withBinaryFile" osfp+ $ withOpenFileEncoding osfp iomode True False closeOnExec (try . act) True)+ >>= either ioError pure++openBinaryFile :: OsPath -> IOMode -> IO Handle+openBinaryFile osfp iomode = augmentError "openBinaryFile" osfp $+ withOpenFileEncoding osfp iomode True False closeOnExec pure False++readFile :: OsPath -> IO BSL.ByteString+readFile fp = withFileNoEncoding' fp ReadMode BSL.hGetContents++readFile'+ :: OsPath -> IO BS.ByteString+readFile' fp = withFileNoEncoding fp ReadMode BS.hGetContents++writeFile :: OsPath -> BSL.ByteString -> IO ()+writeFile fp contents = withFileNoEncoding fp WriteMode (`BSL.hPut` contents)++writeFile'+ :: OsPath -> BS.ByteString -> IO ()+writeFile' fp contents = withFileNoEncoding fp WriteMode (`BS.hPut` contents)++appendFile :: OsPath -> BSL.ByteString -> IO ()+appendFile fp contents = withFileNoEncoding fp AppendMode (`BSL.hPut` contents)++appendFile'+ :: OsPath -> BS.ByteString -> IO ()+appendFile' fp contents = withFileNoEncoding fp AppendMode (`BS.hPut` contents)++{- Re-implementing openTempFile is difficult due to the current+ - structure of file-io. See this issue for discussion about improving+ - that: https://github.com/haskell/file-io/issues/44+ - So, instead this uses noCreateProcessWhile.+ - -}+openTempFile :: OsPath -> OsString -> IO (OsPath, Handle)+openTempFile tmp_dir template = do+#ifdef mingw32_HOST_OS+ (p, h) <- I.openTempFile tmp_dir template+ getLocaleEncoding >>= hSetEncoding h+ pure (p, h)+#else+ noCreateProcessWhile $ do+ (p, h) <- I.openTempFile tmp_dir template+ fd <- handleToFd h+ setFdOption fd CloseOnExec True+ h' <- fdToHandle fd+ getLocaleEncoding >>= hSetEncoding h'+ pure (p, h')+#endif++{- Wrapper around withOpenFile' that sets the locale encoding on the+ - Handle. -}+withOpenFileEncoding :: OsPath -> IOMode -> Bool -> Bool -> Bool -> (Handle -> IO r) -> Bool -> IO r+withOpenFileEncoding fp iomode binary existing cloExec action close_finally =+ withOpenFile' fp iomode binary existing cloExec action' close_finally+ where+ action' h = do+ getLocaleEncoding >>= hSetEncoding h+ action h++{- Variant of withFile above that does not have the overhead of setting the+ - locale encoding. Faster to use when the Handle is not used in a way that+ - needs any encoding. -}+withFileNoEncoding :: OsPath -> IOMode -> (Handle -> IO r) -> IO r+withFileNoEncoding osfp iomode act = (augmentError "withFile" osfp+ $ withOpenFile' osfp iomode False False closeOnExec (try . act) True)+ >>= either ioError pure++{- Variant of withFile' above that does not have the overhead of setting the+ - locale encoding. Faster to use when the Handle is not used in a way that+ - needs any encoding. -}+withFileNoEncoding' :: OsPath -> IOMode -> (Handle -> IO r) -> IO r+withFileNoEncoding' osfp iomode act = (augmentError "withFile'" osfp+ $ withOpenFile' osfp iomode False False closeOnExec (try . act) False)+ >>= either ioError pure++#endif
+ Utility/FileIO/String.hs view
@@ -0,0 +1,38 @@+{- Functions that operate on OsPath, but treat the contents of files as+ - Strings.+ -+ - These functions all set the close-on-exec flag to True, unlike+ - the Prelude versions.+ -+ - Copyright 2025 Joey Hess <id@joeyh.name>+ -+ - License: BSD-2-clause+ -}++{-# OPTIONS_GHC -fno-warn-tabs #-}+{-# LANGUAGE CPP #-}++module Utility.FileIO.String+(+#ifdef WITH_OSPATH+ readFileString,+ writeFileString,+ appendFileString,+#endif+) where++#ifdef WITH_OSPATH+import qualified Utility.FileIO.CloseOnExec as I+import Utility.OsPath (OsPath)+import Prelude (String, IO, (>>=))+import System.IO (IOMode(..), hGetContents, hPutStr)++readFileString :: OsPath -> IO String+readFileString f = I.openFile f ReadMode >>= hGetContents++writeFileString :: OsPath -> String -> IO ()+writeFileString f txt = I.withFile f WriteMode (\hdl -> hPutStr hdl txt)++appendFileString :: OsPath -> String -> IO ()+appendFileString f txt = I.withFile f AppendMode (\hdl -> hPutStr hdl txt)+#endif
Utility/FileMode.hs view
@@ -171,7 +171,7 @@ - When possible, this is done using the umask. - - On a filesystem that does not support file permissions, this is the same- - as writeFile.+ - as writeFileString. -} writeFileProtected :: OsPath -> String -> IO () writeFileProtected file content = writeFileProtected' file
Utility/FileSystemEncoding.hs view
@@ -119,11 +119,14 @@ toRawFilePath :: FilePath -> RawFilePath toRawFilePath = encodeBS -{- Truncates a FilePath to the given number of bytes (or less),+{- Truncates a path to the given number of bytes (or less), - as represented on disk. - - Avoids returning an invalid part of a unicode byte sequence, at the - cost of efficiency when running on a large FilePath.+ -+ - Note that this may return ""! That can happen if it is asked to truncate+ - to eg 1 byte, but the input path starts with a unicode byte sequence. -} truncateFilePath :: Int -> RawFilePath -> RawFilePath #ifndef mingw32_HOST_OS
Utility/FreeDesktop.hs view
@@ -75,7 +75,7 @@ writeDesktopMenuFile :: DesktopEntry -> OsPath -> IO () writeDesktopMenuFile d file = do createDirectoryIfMissing True (takeDirectory file)- writeFile (fromOsPath file) $ buildDesktopMenuFile d+ writeFileString file $ buildDesktopMenuFile d {- Path to use for a desktop menu file, in either the systemDataDir or - the userDataDir -}
Utility/Gpg.hs view
@@ -162,8 +162,10 @@ #ifndef mingw32_HOST_OS let setup = liftIO $ do -- pipe the passphrase into gpg on a fd- (frompipe, topipe) <- System.Posix.IO.createPipe- setFdOption topipe CloseOnExec True+ (frompipe, topipe) <- noCreateProcessWhile $ do+ (frompipe, topipe) <- System.Posix.IO.createPipe+ setFdOption topipe CloseOnExec True+ return (frompipe, topipe) toh <- fdToHandle topipe t <- async $ do B.hPutStr toh (passphrase <> "\n")
Utility/Hash.hs view
@@ -1,4 +1,4 @@-{- Convenience wrapper around cryptonite's hashing.+{- Convenience wrapper around crypton's hashing. - - Copyright 2013-2024 Joey Hess <id@joeyh.name> -@@ -7,7 +7,6 @@ {-# LANGUAGE BangPatterns, PackageImports #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE CPP #-} module Utility.Hash ( sha1,@@ -78,13 +77,8 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.IORef-#ifdef WITH_CRYPTON import "crypton" Crypto.MAC.HMAC hiding (Context) import "crypton" Crypto.Hash-#else-import "cryptonite" Crypto.MAC.HMAC hiding (Context)-import "cryptonite" Crypto.Hash-#endif sha1 :: L.ByteString -> Digest SHA1 sha1 = hashlazy
Utility/HumanTime.hs view
@@ -23,8 +23,6 @@ import Data.Time.Clock import Data.Time.Clock.POSIX (POSIXTime) import Data.Char-import Control.Applicative-import Prelude newtype Duration = Duration { durationSeconds :: Integer } deriving (Eq, Ord, Read, Show)
Utility/IPAddress.hs view
@@ -23,9 +23,7 @@ import Data.Word import Data.Memory.Endian import Data.List-import Control.Applicative import Text.Printf-import Prelude extractIPAddress :: SockAddr -> Maybe String extractIPAddress (SockAddrInet _ ipv4) =
Utility/InodeCache.hs view
@@ -234,7 +234,7 @@ writeSentinalFile :: SentinalFile -> IO () writeSentinalFile s = do F.writeFile' (sentinalFile s) mempty- maybe noop (writeFile (fromOsPath (sentinalCacheFile s)) . showInodeCache)+ maybe noop (writeFileString (sentinalCacheFile s) . showInodeCache) =<< genInodeCache (sentinalFile s) noTSDelta data SentinalStatus = SentinalStatus@@ -263,7 +263,7 @@ Just new -> return $ calc old new where loadoldcache = catchDefaultIO Nothing $- readInodeCache <$> readFile (fromOsPath (sentinalCacheFile s))+ readInodeCache <$> readFileString (sentinalCacheFile s) gennewcache = genInodeCache (sentinalFile s) noTSDelta calc (InodeCache (InodeCachePrim oldinode oldsize oldmtime)) (InodeCache (InodeCachePrim newinode newsize newmtime)) = SentinalStatus (not unchanged) tsdelta
Utility/LinuxMkLibs.hs view
@@ -33,8 +33,6 @@ import System.Posix.Files (isSymbolicLink) import Data.Char import Control.Monad.IfElse-import Control.Applicative-import Prelude {- Installs a library. If the library is a symlink to another file, - install the file it links to, and update the symlink to be relative. -}
Utility/LockFile/PidLock.hs view
@@ -41,6 +41,7 @@ import Utility.Tmp import Utility.RawFilePath import Utility.OsPath+import qualified Utility.FileIO as F import qualified Utility.LockFile.Posix as Posix import System.IO@@ -48,13 +49,12 @@ import System.Posix.IO.ByteString import System.Posix.Files.ByteString import System.Posix.Process+import GHC.IO.Encoding (getLocaleEncoding) import Control.Monad import Control.Monad.IO.Class (liftIO, MonadIO) import Data.Maybe import Data.List import Network.BSD-import Control.Applicative-import Prelude type PidLockFile = OsPath @@ -77,7 +77,7 @@ readPidLock :: PidLockFile -> IO (Maybe PidLock) readPidLock lockfile = (readish =<<)- <$> catchMaybeIO (readFile (fromOsPath lockfile))+ <$> catchMaybeIO (F.readFileString lockfile) -- To avoid races when taking over a stale pid lock, a side lock is used. -- This is a regular posix exclusive lock.@@ -210,10 +210,13 @@ let setup = do fd <- openFdWithMode dest' WriteOnly (Just $ combineModes readModes)- (defaultFileFlags {exclusive = True})- fdToHandle fd+ (defaultFileFlags { exclusive = True })+ (CloseOnExecFlag True)+ h <- fdToHandle fd+ getLocaleEncoding >>= hSetEncoding h+ return h let cleanup = hClose- let go h = readFile (fromOsPath src) >>= hPutStr h+ let go h = F.readFileString src >>= hPutStr h bracket setup cleanup go getFileStatus dest' where
Utility/LockFile/Posix.hs view
@@ -74,11 +74,10 @@ -- Close on exec flag is set so child processes do not inherit the lock. openLockFile :: LockRequest -> Maybe ModeSetter -> LockFile -> IO Fd-openLockFile lockreq filemode lockfile = do- l <- applyModeSetter filemode lockfile $ \filemode' ->- openFdWithMode (fromOsPath lockfile) openfor filemode' defaultFileFlags- setFdOption l CloseOnExec True- return l+openLockFile lockreq filemode lockfile =+ applyModeSetter filemode lockfile $ \filemode' ->+ openFdWithMode (fromOsPath lockfile) openfor filemode'+ defaultFileFlags (CloseOnExecFlag True) where openfor = case lockreq of ReadLock -> ReadOnly
Utility/LockPool/LockHandle.hs view
@@ -25,7 +25,6 @@ import Control.Concurrent.STM import Control.Monad.Catch import Control.Monad.IO.Class (liftIO, MonadIO)-import Prelude data LockHandle = LockHandle P.LockHandle FileLockOps
Utility/LockPool/PidLock.hs view
@@ -25,15 +25,12 @@ import Utility.LockPool.LockHandle import Utility.ThreadScheduler -import System.IO import System.Posix import Control.Concurrent.STM import Data.Maybe import Control.Monad import Control.Monad.Catch import Control.Monad.IO.Class-import Control.Applicative-import Prelude -- Does locking using a pid lock, blocking until the lock is available -- or the Seconds timeout if the pid lock is held by another process.
Utility/LockPool/Posix.hs view
@@ -26,11 +26,7 @@ import Utility.LockPool.LockHandle import Utility.FileMode -import System.IO import System.Posix-import Data.Maybe-import Control.Applicative-import Prelude -- Takes a shared lock, blocking until the lock is available. lockShared :: Maybe ModeSetter -> LockFile -> IO LockHandle
Utility/MagicWormhole.hs view
@@ -38,8 +38,6 @@ import Control.Concurrent.Async import Data.Char import Data.List-import Control.Applicative-import Prelude -- | A Magic Wormhole code. newtype Code = Code String
Utility/Misc.hs view
@@ -34,12 +34,10 @@ import Data.Char import Data.List import System.Exit-import Control.Applicative import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.Char8 as L8-import Prelude {- A version of hgetContents that is not lazy. Ensures file is - all read before it gets closed. -}@@ -110,7 +108,7 @@ fileLines' = S8.lines #endif --- One windows, writeFile does NewlineMode translation,+-- On windows, writeFile does NewlineMode translation, -- adding CR before LF. When converting to ByteString, use this to emulate that. linesFile :: L.ByteString -> L.ByteString #ifndef mingw32_HOST_OS
Utility/MoveFile.hs view
@@ -15,7 +15,6 @@ import Control.Monad import System.IO.Error-import Prelude #ifndef mingw32_HOST_OS import System.PosixCompat.Files (isDirectory)
Utility/Network.hs view
@@ -12,9 +12,6 @@ import Utility.Process import Utility.Exception -import Control.Applicative-import Prelude- {- Haskell lacks uname(2) bindings, except in the - Bindings.Uname addon. Rather than depend on that, - use uname -n when available. -}
Utility/OpenFd.hs view
@@ -1,6 +1,6 @@ {- openFd wrapper to support old versions of unix package. -- - Copyright 2023 Joey Hess <id@joeyh.name>+ - Copyright 2023-2025 Joey Hess <id@joeyh.name> - - License: BSD-2-clause -}@@ -17,12 +17,17 @@ import Utility.RawFilePath -openFdWithMode :: RawFilePath -> OpenMode -> Maybe FileMode -> OpenFileFlags -> IO Fd+newtype CloseOnExecFlag = CloseOnExecFlag Bool++openFdWithMode :: RawFilePath -> OpenMode -> Maybe FileMode -> OpenFileFlags -> CloseOnExecFlag -> IO Fd+openFdWithMode f openmode filemode flags (CloseOnExecFlag closeonexec) = do #if MIN_VERSION_unix(2,8,0)-openFdWithMode f openmode filemode flags = - openFd f openmode (flags { creat = filemode })+ openFd f openmode (flags { creat = filemode, cloexec = closeonexec }) #else-openFdWithMode = openFd+ fd <- openFd f openmode filemode flags+ when closeonexec $+ setFdOption fd CloseOnExec True+ return fd #endif #endif
Utility/OpenFile.hs view
@@ -31,7 +31,7 @@ -} openFileBeingWritten :: RawFilePath -> IO Handle openFileBeingWritten f = do- fd <- openFdWithMode f ReadOnly Nothing defaultFileFlags+ fd <- openFdWithMode f ReadOnly Nothing defaultFileFlags (CloseOnExecFlag True) (fd', fdtype) <- mkFD (fromIntegral fd) ReadMode (Just (Stream, 0, 0)) False False mkHandleFromFD fd' fdtype (fromRawFilePath f) ReadMode False Nothing
Utility/OptParse.hs view
@@ -11,8 +11,6 @@ ) where import Options.Applicative-import Data.Monoid-import Prelude -- | A switch that can be enabled using --foo and disabled using --no-foo. --
Utility/Path.hs view
@@ -31,8 +31,6 @@ import Data.List import Data.Maybe import Control.Monad-import Control.Applicative-import Prelude import Author import Utility.Monad
Utility/Path/AbsRel.hs view
@@ -18,8 +18,6 @@ ) where import qualified Data.ByteString as B-import Control.Applicative-import Prelude import Utility.Path import Utility.UserInfo
Utility/Path/Max.hs view
@@ -13,9 +13,6 @@ #ifndef mingw32_HOST_OS import Utility.Exception import System.Posix.Files-import Data.List-import Control.Applicative-import Prelude #endif {- Maximum size to use for a file in a specified directory.
Utility/Path/Tests.hs view
@@ -17,11 +17,6 @@ prop_dirContains_regressionTest, ) where -import Data.List-import Data.Maybe-import Control.Applicative-import Prelude- import Common import Utility.QuickCheck import qualified Utility.OsString as OS
Utility/Process.hs view
@@ -1,7 +1,8 @@ {- System.Process enhancements, including additional ways of running- - processes, and logging.+ - processes, logging, and amelorations for cases where FDs are not able to+ - be opened with close-on-exec. -- - Copyright 2012-2020 Joey Hess <id@joeyh.name>+ - Copyright 2012-2025 Joey Hess <id@joeyh.name> - - License: BSD-2-clause -}@@ -21,6 +22,7 @@ forceSuccessProcess', checkSuccessProcess, withNullHandle,+ noCreateProcessWhile, createProcess, withCreateProcess, waitForProcess,@@ -36,7 +38,7 @@ ) where import qualified Utility.Process.Shim-import Utility.Process.Shim as X (CreateProcess(..), ProcessHandle, StdStream(..), CmdSpec(..), proc, getPid, getProcessExitCode, shell, terminateProcess, interruptProcessGroupOf)+import Utility.Process.Shim as X (CreateProcess(..), ProcessHandle, StdStream(..), CmdSpec(..), proc, getPid, getProcessExitCode, shell, terminateProcess, interruptProcessGroupOf, Pid) import Utility.Misc import Utility.Exception import Utility.Monad@@ -46,7 +48,9 @@ import System.IO import Control.Monad.IO.Class import Control.Concurrent.Async+import Control.Concurrent import qualified Data.ByteString as S+import System.IO.Unsafe (unsafePerformIO) data StdHandle = StdinHandle | StdoutHandle | StderrHandle deriving (Eq)@@ -173,9 +177,34 @@ (Just from, Just to, _, pid) <- createProcess p return (pid, to, from) --- | Wrapper around 'System.Process.createProcess' that does debug logging.+-- | Runs an action, preventing any new processes from being started+-- until it is finished.+--+-- Unfortunately, Haskell has a pervasive problem with the close-on-exec+-- flag not being set when opening files. It's also difficult to portably+-- dup or pipe a FD with the close-on-exec flag set. So, this can be used+-- to run an action that opens a FD, and then calls setFdOption to set the+-- close-on-exec flag, without risking a race with a process being forked+-- at the same time.+--+-- Note that only one of these actions can run at a time, and long-duration+-- actions are not advisable.+noCreateProcessWhile :: (MonadIO m, MonadMask m) => (m a) -> m a+noCreateProcessWhile = bracket setup cleanup . const+ where+ setup = liftIO $ takeMVar createProcessSem+ cleanup () = liftIO $ putMVar createProcessSem ()++-- | A shared global MVar. Processes are not created while it is empty.+{-# NOINLINE createProcessSem #-}+createProcessSem :: MVar ()+createProcessSem = unsafePerformIO $ newMVar ()++-- | Wrapper around 'System.Process.createProcess'. +-- This adds debug logging, and avoids starting a process when in a+-- noCreateProcessWhile block. createProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)-createProcess p = do+createProcess p = noCreateProcessWhile $ do r@(_, _, _, h) <- Utility.Process.Shim.createProcess p debugProcess p h return r
Utility/Process/Transcript.hs view
@@ -23,11 +23,9 @@ #ifndef mingw32_HOST_OS import Control.Exception import qualified System.Posix.IO-#else-import Control.Applicative+import GHC.IO.Encoding (getLocaleEncoding) #endif import Data.Maybe-import Prelude -- | Runs a process and returns a transcript combining its stdout and -- stderr, and whether it succeeded or failed.@@ -45,12 +43,15 @@ #ifndef mingw32_HOST_OS {- This implementation interleves stdout and stderr in exactly the order - the process writes them. -}- let setup = do+ let setup = noCreateProcessWhile $ do (readf, writef) <- System.Posix.IO.createPipe System.Posix.IO.setFdOption readf System.Posix.IO.CloseOnExec True System.Posix.IO.setFdOption writef System.Posix.IO.CloseOnExec True readh <- System.Posix.IO.fdToHandle readf writeh <- System.Posix.IO.fdToHandle writef+ enc <- getLocaleEncoding+ hSetEncoding readh enc+ hSetEncoding writeh enc return (readh, writeh) let cleanup (readh, writeh) = do hClose readh
Utility/QuickCheck.hs view
@@ -24,7 +24,6 @@ import Data.Char import System.Posix.Types import Data.List.NonEmpty (NonEmpty(..))-import Prelude {- A String, but Arbitrary is limited to ascii. -
Utility/SafeCommand.hs view
@@ -26,9 +26,6 @@ import System.Exit import System.FilePath import Data.Char-import Data.List-import Control.Applicative-import Prelude -- | Parameters that can be passed to a shell command. data CommandParam
Utility/Scheduled.hs view
@@ -40,8 +40,6 @@ import Data.Time.Calendar.OrdinalDate import Data.Time.Format () import Data.Char-import Control.Applicative-import Prelude {- Some sort of scheduled event. -} data Schedule = Schedule Recurrence ScheduledTime
Utility/Scheduled/QuickCheck.hs view
@@ -12,9 +12,6 @@ import Utility.Scheduled import Utility.QuickCheck -import Control.Applicative-import Prelude- instance Arbitrary Schedule where arbitrary = Schedule <$> arbitrary <*> arbitrary
Utility/Shell.hs view
@@ -19,6 +19,7 @@ import Utility.Path import Utility.Exception import Utility.PartialPrelude+import Utility.FileIO (readFileString) #endif shellPath :: FilePath@@ -37,7 +38,7 @@ #ifndef mingw32_HOST_OS defcmd #else- l <- catchDefaultIO Nothing $ headMaybe . lines <$> readFile (fromOsPath f)+ l <- catchDefaultIO Nothing $ headMaybe . lines <$> readFileString f case l of Just ('#':'!':rest) -> case words rest of [] -> defcmd
Utility/ShellEscape.hs view
@@ -18,10 +18,9 @@ import Author import Utility.QuickCheck import Utility.Split-import Data.Function +import Data.Function import Data.List-import Prelude copyright :: Copyright copyright = author JoeyHess (2000+30-20)
Utility/StatelessOpenPGP.hs view
@@ -141,8 +141,10 @@ #ifndef mingw32_HOST_OS let setup = liftIO $ do -- pipe the passphrase in on a fd- (frompipe, topipe) <- System.Posix.IO.createPipe- setFdOption topipe CloseOnExec True+ (frompipe, topipe) <- noCreateProcessWhile $ do+ (frompipe, topipe) <- System.Posix.IO.createPipe+ setFdOption topipe CloseOnExec True+ return (frompipe, topipe) toh <- fdToHandle topipe t <- async $ do B.hPutStr toh (password <> "\n")
Utility/Tmp.hs view
@@ -49,13 +49,13 @@ let loc = ioeGetLocation e ++ " template " ++ decodeBS (fromOsPath template) in annotateIOError e loc Nothing Nothing -{- Runs an action like writeFile, writing to a temp file first and+{- Runs an action like writeFileString, writing to a temp file first and - then moving it into place. The temp file is stored in the same - directory as the final file to avoid cross-device renames. - - While this uses a temp file, the file will end up with the same- - mode as it would when using writeFile, unless the writer action changes- - it.+ - mode as it would when using writeFileString, unless the writer action+ - changes it. -} viaTmp :: (MonadMask m, MonadIO m) => (OsPath -> v -> m ()) -> OsPath -> v -> m () viaTmp a file content = bracketIO setup cleanup use@@ -116,17 +116,32 @@ #ifndef mingw32_HOST_OS relatedTemplate' f | len > templateAddedLength = - {- Some filesystems like FAT have issues with filenames- - ending in ".", and others like VFAT don't allow a- - filename to end with trailing whitespace, so avoid- - truncating a filename to end that way. -}- B.dropWhileEnd disallowed $- truncateFilePath (len - templateAddedLength) f+ let p = fixend $ truncateFilePath (len - templateAddedLength) f+ in if B.null p+ then "t"+ else p | otherwise = f where len = B.length f- disallowed c = c == dot || isSpace (chr (fromIntegral c))+ {- Some filesystems like FAT have issues with filenames+ - ending in ".", and others like VFAT don't allow a+ - filename to end with trailing whitespace, so avoid+ - truncating a filename to end that way.+ -+ - Checking with unsnoc for the path to end with a disallowed+ - character doesn't take wide characters into account,+ - so will have false positives, but it is fast.+ -}+ fixend p = case B.unsnoc p of+ Just (_, c) | disallowed c -> + -- Convert to String to take wide characters into+ -- account.+ toRawFilePath $ reverse $+ dropWhile (disallowed . fromIntegral . ord) $+ reverse $ fromRawFilePath p+ _ -> p dot = fromIntegral (ord '.')+ disallowed c = c == dot || isSpace (chr (fromIntegral c)) #else -- Avoids a test suite failure on windows, reason unknown, but -- best to keep paths short on windows anyway.
Utility/Tor.hs view
@@ -71,7 +71,7 @@ addHiddenService :: AppName -> UserID -> UniqueIdent -> IO (OnionAddress, OnionPort) addHiddenService appname uid ident = do prepHiddenServiceSocketDir appname uid ident- ls <- lines <$> (readFile . fromOsPath =<< findTorrc)+ ls <- lines <$> (readFileString =<< findTorrc) let portssocks = mapMaybe (parseportsock . separate isSpace) ls case filter (\(_, s) -> s == fromOsPath sockfile) portssocks of ((p, _s):_) -> waithiddenservice 1 p@@ -80,7 +80,7 @@ let newport = fromMaybe (error "internal") $ headMaybe $ filter (`notElem` map fst portssocks) highports torrc <- findTorrc- writeFile (fromOsPath torrc) $ unlines $+ writeFileString torrc $ unlines $ ls ++ [ "" , "HiddenServiceDir " ++ fromOsPath (hiddenServiceDir appname uid ident)@@ -112,7 +112,7 @@ waithiddenservice :: Int -> OnionPort -> IO (OnionAddress, OnionPort) waithiddenservice 0 _ = giveup "tor failed to create hidden service, perhaps the tor service is not running" waithiddenservice n p = do- v <- tryIO $ readFile $ fromOsPath $+ v <- tryIO $ readFileString $ hiddenServiceHostnameFile appname uid ident case v of Right s | ".onion\n" `isSuffixOf` s ->@@ -152,7 +152,7 @@ getHiddenServiceSocketFile :: AppName -> UserID -> UniqueIdent -> IO (Maybe OsPath) getHiddenServiceSocketFile _appname uid ident = parse . map words . lines <$> catchDefaultIO "" - (readFile . fromOsPath =<< findTorrc)+ (readFileString =<< findTorrc) where parse [] = Nothing parse (("HiddenServiceDir":hsdir:[]):("HiddenServicePort":_hsport:hsaddr:[]):rest)
Utility/UserInfo.hs view
@@ -18,14 +18,11 @@ import Utility.Exception #ifndef mingw32_HOST_OS import Utility.Data-import Control.Applicative import System.Posix.User #if MIN_VERSION_unix(2,8,0) import System.Posix.User.ByteString (UserEntry) #endif #endif--import Prelude {- Current user's home directory. -
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 10.20250828+Version: 10.20250925 Cabal-Version: 1.12 License: AGPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -169,14 +169,12 @@ Flag MagicMime Description: Use libmagic to determine file MIME types -Flag Crypton- Description: Use the crypton library rather than the no longer maintained cryptonite- Flag Servant Description: Use the servant library, enabling using annex+http urls and git-annex p2phttp Flag OsPath Description: Use the os-string library and related libraries, for faster filename manipulation+ Default: True Flag Benchmark Description: Enable benchmarking@@ -205,7 +203,7 @@ time (>= 1.9.1), directory (>= 1.2.7.0), async,- utf8-string,+ utf8-string (>= 1.0.0), Cabal (< 4.0) Executable git-annex@@ -233,7 +231,7 @@ IfElse, monad-logger (>= 0.3.10), free,- utf8-string,+ utf8-string (>= 1.0.0), bytestring, text, sandi,@@ -283,7 +281,8 @@ network (>= 3.0.0.0), network-bsd, git-lfs (>= 1.2.0),- clock (>= 0.3.0)+ clock (>= 0.3.0),+ crypton CC-Options: -Wall GHC-Options: -Wall -fno-warn-tabs -Wincomplete-uni-patterns Default-Language: Haskell2010@@ -310,12 +309,6 @@ if os(linux) || os(freebsd) GHC-Options: -optl-Wl,--as-needed - if flag(Crypton)- Build-Depends: crypton- CPP-Options: -DWITH_CRYPTON- else- Build-Depends: cryptonite (>= 0.23)- if flag(Servant) Build-Depends: servant,@@ -1147,6 +1140,8 @@ Utility.Su Utility.SystemDirectory Utility.FileIO+ Utility.FileIO.CloseOnExec+ Utility.FileIO.String Utility.Terminal Utility.TimeStamp Utility.TList
stack.yaml view
@@ -9,7 +9,6 @@ dbus: false debuglocks: false benchmark: true- crypton: true servant: true ospath: true packages: