more OsPath conversion
Sponsored-by: Leon Schuermann
This commit is contained in:
parent
ee0964e61b
commit
f3539efc16
18 changed files with 156 additions and 153 deletions
2
Git.hs
2
Git.hs
|
@ -38,12 +38,10 @@ module Git (
|
||||||
relPath,
|
relPath,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
|
||||||
import Network.URI (uriPath, uriScheme, unEscapeString)
|
import Network.URI (uriPath, uriScheme, unEscapeString)
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
import System.Posix.Files
|
import System.Posix.Files
|
||||||
#endif
|
#endif
|
||||||
import qualified System.FilePath.ByteString as P
|
|
||||||
|
|
||||||
import Common
|
import Common
|
||||||
import Git.Types
|
import Git.Types
|
||||||
|
|
|
@ -49,7 +49,6 @@ import qualified Utility.FileIO as F
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified System.FilePath.ByteString as P
|
|
||||||
|
|
||||||
{- Given a set of bad objects found by git fsck, which may not
|
{- Given a set of bad objects found by git fsck, which may not
|
||||||
- be complete, finds and removes all corrupt objects. -}
|
- be complete, finds and removes all corrupt objects. -}
|
||||||
|
@ -59,9 +58,10 @@ cleanCorruptObjects fsckresults r = do
|
||||||
mapM_ removeLoose (S.toList $ knownMissing fsckresults)
|
mapM_ removeLoose (S.toList $ knownMissing fsckresults)
|
||||||
mapM_ removeBad =<< listLooseObjectShas r
|
mapM_ removeBad =<< listLooseObjectShas r
|
||||||
where
|
where
|
||||||
removeLoose s = removeWhenExistsWith R.removeLink (looseObjectFile r s)
|
removeLoose s = removeWhenExistsWith R.removeLink $
|
||||||
|
fromOsPath $ looseObjectFile r s
|
||||||
removeBad s = do
|
removeBad s = do
|
||||||
void $ tryIO $ allowRead $ looseObjectFile r s
|
void $ tryIO $ allowRead $ fromOsPath $ looseObjectFile r s
|
||||||
whenM (isMissing s r) $
|
whenM (isMissing s r) $
|
||||||
removeLoose s
|
removeLoose s
|
||||||
|
|
||||||
|
@ -85,20 +85,20 @@ explodePacks r = go =<< listPackFiles r
|
||||||
putStrLn "Unpacking all pack files."
|
putStrLn "Unpacking all pack files."
|
||||||
forM_ packs $ \packfile -> do
|
forM_ packs $ \packfile -> do
|
||||||
-- Just in case permissions are messed up.
|
-- Just in case permissions are messed up.
|
||||||
allowRead packfile
|
allowRead (fromOsPath packfile)
|
||||||
-- May fail, if pack file is corrupt.
|
-- May fail, if pack file is corrupt.
|
||||||
void $ tryIO $
|
void $ tryIO $
|
||||||
pipeWrite [Param "unpack-objects", Param "-r"] r' $ \h ->
|
pipeWrite [Param "unpack-objects", Param "-r"] r' $ \h ->
|
||||||
L.hPut h =<< F.readFile (toOsPath packfile)
|
L.hPut h =<< F.readFile packfile
|
||||||
objs <- emptyWhenDoesNotExist (dirContentsRecursive tmpdir)
|
objs <- emptyWhenDoesNotExist (dirContentsRecursive tmpdir)
|
||||||
forM_ objs $ \objfile -> do
|
forM_ objs $ \objfile -> do
|
||||||
f <- relPathDirToFile tmpdir objfile
|
f <- relPathDirToFile tmpdir objfile
|
||||||
let dest = objectsDir r P.</> f
|
let dest = objectsDir r </> f
|
||||||
createDirectoryIfMissing True (parentDir dest)
|
createDirectoryIfMissing True (parentDir dest)
|
||||||
moveFile objfile dest
|
moveFile (fromOsPath objfile) (fromOsPath dest)
|
||||||
forM_ packs $ \packfile -> do
|
forM_ packs $ \packfile -> do
|
||||||
removeWhenExistsWith R.removeLink packfile
|
removeWhenExistsWith R.removeLink (fromOsPath packfile)
|
||||||
removeWhenExistsWith R.removeLink (packIdxFile packfile)
|
removeWhenExistsWith R.removeLink (fromOsPath (packIdxFile packfile))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
{- Try to retrieve a set of missing objects, from the remotes of a
|
{- Try to retrieve a set of missing objects, from the remotes of a
|
||||||
|
@ -115,7 +115,7 @@ retrieveMissingObjects missing referencerepo r
|
||||||
unlessM (boolSystem "git" [Param "init", File (fromOsPath tmpdir)]) $
|
unlessM (boolSystem "git" [Param "init", File (fromOsPath tmpdir)]) $
|
||||||
giveup $ "failed to create temp repository in " ++ fromOsPath tmpdir
|
giveup $ "failed to create temp repository in " ++ fromOsPath tmpdir
|
||||||
tmpr <- Config.read =<< Construct.fromPath tmpdir
|
tmpr <- Config.read =<< Construct.fromPath tmpdir
|
||||||
let repoconfig r' = localGitDir r' </> "config"
|
let repoconfig r' = localGitDir r' </> literalOsPath "config"
|
||||||
whenM (doesFileExist (repoconfig r)) $
|
whenM (doesFileExist (repoconfig r)) $
|
||||||
F.readFile (repoconfig r) >>= F.writeFile (repoconfig tmpr)
|
F.readFile (repoconfig r) >>= F.writeFile (repoconfig tmpr)
|
||||||
rs <- Construct.fromRemotes r
|
rs <- Construct.fromRemotes r
|
||||||
|
@ -251,7 +251,7 @@ getAllRefs r = getAllRefs' (localGitDir r </> literalOsPath "refs")
|
||||||
getAllRefs' :: OsPath -> IO [Ref]
|
getAllRefs' :: OsPath -> IO [Ref]
|
||||||
getAllRefs' refdir = do
|
getAllRefs' refdir = do
|
||||||
let topsegs = length (splitPath refdir) - 1
|
let topsegs = length (splitPath refdir) - 1
|
||||||
let toref = Ref . toInternalGitPath
|
let toref = Ref . fromOsPath . toInternalGitPath
|
||||||
. joinPath . drop topsegs . splitPath
|
. joinPath . drop topsegs . splitPath
|
||||||
map toref <$> emptyWhenDoesNotExist (dirContentsRecursive refdir)
|
map toref <$> emptyWhenDoesNotExist (dirContentsRecursive refdir)
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ explodePackedRefsFile r = do
|
||||||
writeFile (fromOsPath dest) (fromRef sha)
|
writeFile (fromOsPath dest) (fromRef sha)
|
||||||
|
|
||||||
packedRefsFile :: Repo -> OsPath
|
packedRefsFile :: Repo -> OsPath
|
||||||
packedRefsFile r = localGitDir r </> "packed-refs"
|
packedRefsFile r = localGitDir r </> literalOsPath "packed-refs"
|
||||||
|
|
||||||
parsePacked :: String -> Maybe (Sha, Ref)
|
parsePacked :: String -> Maybe (Sha, Ref)
|
||||||
parsePacked l = case words l of
|
parsePacked l = case words l of
|
||||||
|
@ -286,7 +286,8 @@ parsePacked l = case words l of
|
||||||
{- git-branch -d cannot be used to remove a branch that is directly
|
{- git-branch -d cannot be used to remove a branch that is directly
|
||||||
- pointing to a corrupt commit. -}
|
- pointing to a corrupt commit. -}
|
||||||
nukeBranchRef :: Branch -> Repo -> IO ()
|
nukeBranchRef :: Branch -> Repo -> IO ()
|
||||||
nukeBranchRef b r = removeWhenExistsWith R.removeLink $ localGitDir r P.</> fromRef' b
|
nukeBranchRef b r = removeWhenExistsWith R.removeLink $ fromOsPath $
|
||||||
|
localGitDir r </> toOsPath (fromRef' b)
|
||||||
|
|
||||||
{- Finds the most recent commit to a branch that does not need any
|
{- Finds the most recent commit to a branch that does not need any
|
||||||
- of the missing objects. If the input branch is good as-is, returns it.
|
- of the missing objects. If the input branch is good as-is, returns it.
|
||||||
|
@ -405,7 +406,7 @@ checkIndexFast r = do
|
||||||
length indexcontents `seq` cleanup
|
length indexcontents `seq` cleanup
|
||||||
|
|
||||||
missingIndex :: Repo -> IO Bool
|
missingIndex :: Repo -> IO Bool
|
||||||
missingIndex r = not <$> doesFileExist (localGitDir r </> "index")
|
missingIndex r = not <$> doesFileExist (localGitDir r </> literalOsPath "index")
|
||||||
|
|
||||||
{- Finds missing and ok files staged in the index. -}
|
{- Finds missing and ok files staged in the index. -}
|
||||||
partitionIndex :: Repo -> IO ([LsFiles.StagedDetails], [LsFiles.StagedDetails], IO Bool)
|
partitionIndex :: Repo -> IO ([LsFiles.StagedDetails], [LsFiles.StagedDetails], IO Bool)
|
||||||
|
@ -424,11 +425,11 @@ rewriteIndex r
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
(bad, good, cleanup) <- partitionIndex r
|
(bad, good, cleanup) <- partitionIndex r
|
||||||
unless (null bad) $ do
|
unless (null bad) $ do
|
||||||
removeWhenExistsWith R.removeLink (indexFile r)
|
removeWhenExistsWith R.removeLink (fromOsPath (indexFile r))
|
||||||
UpdateIndex.streamUpdateIndex r
|
UpdateIndex.streamUpdateIndex r
|
||||||
=<< (catMaybes <$> mapM reinject good)
|
=<< (catMaybes <$> mapM reinject good)
|
||||||
void cleanup
|
void cleanup
|
||||||
return $ map (\(file,_, _, _) -> fromRawFilePath file) bad
|
return $ map (\(file,_, _, _) -> fromOsPath file) bad
|
||||||
where
|
where
|
||||||
reinject (file, sha, mode, _) = case toTreeItemType mode of
|
reinject (file, sha, mode, _) = case toTreeItemType mode of
|
||||||
Nothing -> return Nothing
|
Nothing -> return Nothing
|
||||||
|
@ -472,13 +473,13 @@ displayList items header
|
||||||
preRepair :: Repo -> IO ()
|
preRepair :: Repo -> IO ()
|
||||||
preRepair g = do
|
preRepair g = do
|
||||||
unlessM (validhead <$> catchDefaultIO "" (decodeBS <$> safeReadFile headfile)) $ do
|
unlessM (validhead <$> catchDefaultIO "" (decodeBS <$> safeReadFile headfile)) $ do
|
||||||
removeWhenExistsWith R.removeLink headfile
|
removeWhenExistsWith R.removeLink (fromOsPath headfile)
|
||||||
writeFile (fromRawFilePath headfile) "ref: refs/heads/master"
|
writeFile (fromOsPath headfile) "ref: refs/heads/master"
|
||||||
explodePackedRefsFile g
|
explodePackedRefsFile g
|
||||||
unless (repoIsLocalBare g) $
|
unless (repoIsLocalBare g) $
|
||||||
void $ tryIO $ allowWrite $ indexFile g
|
void $ tryIO $ allowWrite $ fromOsPath $ indexFile g
|
||||||
where
|
where
|
||||||
headfile = localGitDir g P.</> "HEAD"
|
headfile = localGitDir g </> literalOsPath "HEAD"
|
||||||
validhead s = "ref: refs/" `isPrefixOf` s
|
validhead s = "ref: refs/" `isPrefixOf` s
|
||||||
|| isJust (extractSha (encodeBS s))
|
|| isJust (extractSha (encodeBS s))
|
||||||
|
|
||||||
|
@ -605,7 +606,7 @@ runRepair' removablebranch fsckresult forced referencerepo g = do
|
||||||
else successfulfinish modifiedbranches
|
else successfulfinish modifiedbranches
|
||||||
|
|
||||||
corruptedindex = do
|
corruptedindex = do
|
||||||
removeWhenExistsWith R.removeLink (indexFile g)
|
removeWhenExistsWith R.removeLink (fromOsPath (indexFile g))
|
||||||
-- The corrupted index can prevent fsck from finding other
|
-- The corrupted index can prevent fsck from finding other
|
||||||
-- problems, so re-run repair.
|
-- problems, so re-run repair.
|
||||||
fsckresult' <- findBroken False False g
|
fsckresult' <- findBroken False False g
|
||||||
|
|
|
@ -76,14 +76,14 @@ doMerge hashhandle ch differ repo streamer = do
|
||||||
void $ cleanup
|
void $ cleanup
|
||||||
where
|
where
|
||||||
go [] = noop
|
go [] = noop
|
||||||
go (info:file:rest) = mergeFile info file hashhandle ch >>=
|
go (info:file:rest) = mergeFile info (toOsPath file) hashhandle ch >>=
|
||||||
maybe (go rest) (\l -> streamer l >> go rest)
|
maybe (go rest) (\l -> streamer l >> go rest)
|
||||||
go (_:[]) = giveup $ "parse error " ++ show differ
|
go (_:[]) = giveup $ "parse error " ++ show differ
|
||||||
|
|
||||||
{- Given an info line from a git raw diff, and the filename, generates
|
{- Given an info line from a git raw diff, and the filename, generates
|
||||||
- a line suitable for update-index that union merges the two sides of the
|
- a line suitable for update-index that union merges the two sides of the
|
||||||
- diff. -}
|
- diff. -}
|
||||||
mergeFile :: S.ByteString -> RawFilePath -> HashObjectHandle -> CatFileHandle -> IO (Maybe L.ByteString)
|
mergeFile :: S.ByteString -> OsPath -> HashObjectHandle -> CatFileHandle -> IO (Maybe L.ByteString)
|
||||||
mergeFile info file hashhandle h = case S8.words info of
|
mergeFile info file hashhandle h = case S8.words info of
|
||||||
[_colonmode, _bmode, asha, bsha, _status] ->
|
[_colonmode, _bmode, asha, bsha, _status] ->
|
||||||
case filter (`notElem` nullShas) [Ref asha, Ref bsha] of
|
case filter (`notElem` nullShas) [Ref asha, Ref bsha] of
|
||||||
|
|
|
@ -22,6 +22,7 @@ module Utility.DirWatcher (
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Utility.DirWatcher.Types
|
import Utility.DirWatcher.Types
|
||||||
|
import Utility.OsPath
|
||||||
|
|
||||||
#if WITH_INOTIFY
|
#if WITH_INOTIFY
|
||||||
import qualified Utility.DirWatcher.INotify as INotify
|
import qualified Utility.DirWatcher.INotify as INotify
|
||||||
|
@ -40,7 +41,7 @@ import qualified Utility.DirWatcher.Win32Notify as Win32Notify
|
||||||
import qualified System.Win32.Notify as Win32Notify
|
import qualified System.Win32.Notify as Win32Notify
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
type Pruner = FilePath -> Bool
|
type Pruner = OsPath -> Bool
|
||||||
|
|
||||||
canWatch :: Bool
|
canWatch :: Bool
|
||||||
#if (WITH_INOTIFY || WITH_KQUEUE || WITH_FSEVENTS || WITH_WIN32NOTIFY)
|
#if (WITH_INOTIFY || WITH_KQUEUE || WITH_FSEVENTS || WITH_WIN32NOTIFY)
|
||||||
|
@ -112,7 +113,7 @@ modifyTracked = error "modifyTracked not defined"
|
||||||
- to shutdown later. -}
|
- to shutdown later. -}
|
||||||
#if WITH_INOTIFY
|
#if WITH_INOTIFY
|
||||||
type DirWatcherHandle = INotify.INotify
|
type DirWatcherHandle = INotify.INotify
|
||||||
watchDir :: FilePath -> Pruner -> Bool -> WatchHooks -> (IO () -> IO ()) -> IO DirWatcherHandle
|
watchDir :: OsPath -> Pruner -> Bool -> WatchHooks -> (IO () -> IO ()) -> IO DirWatcherHandle
|
||||||
watchDir dir prune scanevents hooks runstartup = do
|
watchDir dir prune scanevents hooks runstartup = do
|
||||||
i <- INotify.initINotify
|
i <- INotify.initINotify
|
||||||
runstartup $ INotify.watchDir i dir prune scanevents hooks
|
runstartup $ INotify.watchDir i dir prune scanevents hooks
|
||||||
|
@ -120,14 +121,14 @@ watchDir dir prune scanevents hooks runstartup = do
|
||||||
#else
|
#else
|
||||||
#if WITH_KQUEUE
|
#if WITH_KQUEUE
|
||||||
type DirWatcherHandle = ThreadId
|
type DirWatcherHandle = ThreadId
|
||||||
watchDir :: FilePath -> Pruner -> Bool -> WatchHooks -> (IO Kqueue.Kqueue -> IO Kqueue.Kqueue) -> IO DirWatcherHandle
|
watchDir :: OsPath -> Pruner -> Bool -> WatchHooks -> (IO Kqueue.Kqueue -> IO Kqueue.Kqueue) -> IO DirWatcherHandle
|
||||||
watchDir dir prune _scanevents hooks runstartup = do
|
watchDir dir prune _scanevents hooks runstartup = do
|
||||||
kq <- runstartup $ Kqueue.initKqueue dir prune
|
kq <- runstartup $ Kqueue.initKqueue dir prune
|
||||||
forkIO $ Kqueue.runHooks kq hooks
|
forkIO $ Kqueue.runHooks kq hooks
|
||||||
#else
|
#else
|
||||||
#if WITH_FSEVENTS
|
#if WITH_FSEVENTS
|
||||||
type DirWatcherHandle = FSEvents.EventStream
|
type DirWatcherHandle = FSEvents.EventStream
|
||||||
watchDir :: FilePath -> Pruner -> Bool -> WatchHooks -> (IO FSEvents.EventStream -> IO FSEvents.EventStream) -> IO DirWatcherHandle
|
watchDir :: OsPath -> Pruner -> Bool -> WatchHooks -> (IO FSEvents.EventStream -> IO FSEvents.EventStream) -> IO DirWatcherHandle
|
||||||
watchDir dir prune scanevents hooks runstartup =
|
watchDir dir prune scanevents hooks runstartup =
|
||||||
runstartup $ FSEvents.watchDir dir prune scanevents hooks
|
runstartup $ FSEvents.watchDir dir prune scanevents hooks
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -47,7 +47,7 @@ import Control.Exception (throw)
|
||||||
- So this will fail if there are too many subdirectories. The
|
- So this will fail if there are too many subdirectories. The
|
||||||
- errHook is called when this happens.
|
- errHook is called when this happens.
|
||||||
-}
|
-}
|
||||||
watchDir :: INotify -> FilePath -> (FilePath -> Bool) -> Bool -> WatchHooks -> IO ()
|
watchDir :: INotify -> OsPath -> (OsPath -> Bool) -> Bool -> WatchHooks -> IO ()
|
||||||
watchDir i dir ignored scanevents hooks
|
watchDir i dir ignored scanevents hooks
|
||||||
| ignored dir = noop
|
| ignored dir = noop
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
|
@ -56,10 +56,10 @@ watchDir i dir ignored scanevents hooks
|
||||||
lock <- newLock
|
lock <- newLock
|
||||||
let handler event = withLock lock (void $ go event)
|
let handler event = withLock lock (void $ go event)
|
||||||
flip catchNonAsync failedwatch $ do
|
flip catchNonAsync failedwatch $ do
|
||||||
void (addWatch i watchevents (toInternalFilePath dir) handler)
|
void (addWatch i watchevents (fromOsPath dir) handler)
|
||||||
`catchIO` failedaddwatch
|
`catchIO` failedaddwatch
|
||||||
withLock lock $
|
withLock lock $
|
||||||
mapM_ scan =<< filter (not . dirCruft . toRawFilePath) <$>
|
mapM_ scan =<< filter (`notElem` dirCruft) <$>
|
||||||
getDirectoryContents dir
|
getDirectoryContents dir
|
||||||
where
|
where
|
||||||
recurse d = watchDir i d ignored scanevents hooks
|
recurse d = watchDir i d ignored scanevents hooks
|
||||||
|
@ -108,22 +108,21 @@ watchDir i dir ignored scanevents hooks
|
||||||
runhook addHook f ms
|
runhook addHook f ms
|
||||||
_ -> noop
|
_ -> noop
|
||||||
where
|
where
|
||||||
f = fromInternalFilePath fi
|
f = toOsPath fi
|
||||||
|
|
||||||
-- Closing a file is assumed to mean it's done being written,
|
-- Closing a file is assumed to mean it's done being written,
|
||||||
-- so a new add event is sent.
|
-- so a new add event is sent.
|
||||||
go (Closed { isDirectory = False, maybeFilePath = Just fi }) =
|
go (Closed { isDirectory = False, maybeFilePath = Just fi }) =
|
||||||
checkfiletype Files.isRegularFile addHook $
|
checkfiletype Files.isRegularFile addHook (toOsPath fi)
|
||||||
fromInternalFilePath fi
|
|
||||||
|
|
||||||
-- When a file or directory is moved in, scan it to add new
|
-- When a file or directory is moved in, scan it to add new
|
||||||
-- stuff.
|
-- stuff.
|
||||||
go (MovedIn { filePath = fi }) = scan $ fromInternalFilePath fi
|
go (MovedIn { filePath = fi }) = scan (toOsPath fi)
|
||||||
go (MovedOut { isDirectory = isd, filePath = fi })
|
go (MovedOut { isDirectory = isd, filePath = fi })
|
||||||
| isd = runhook delDirHook f Nothing
|
| isd = runhook delDirHook f Nothing
|
||||||
| otherwise = runhook delHook f Nothing
|
| otherwise = runhook delHook f Nothing
|
||||||
where
|
where
|
||||||
f = fromInternalFilePath fi
|
f = toOsPath fi
|
||||||
|
|
||||||
-- Verify that the deleted item really doesn't exist,
|
-- Verify that the deleted item really doesn't exist,
|
||||||
-- since there can be spurious deletion events for items
|
-- since there can be spurious deletion events for items
|
||||||
|
@ -134,11 +133,11 @@ watchDir i dir ignored scanevents hooks
|
||||||
| otherwise = guarded $ runhook delHook f Nothing
|
| otherwise = guarded $ runhook delHook f Nothing
|
||||||
where
|
where
|
||||||
guarded = unlessM (filetype (const True) f)
|
guarded = unlessM (filetype (const True) f)
|
||||||
f = fromInternalFilePath fi
|
f = toOsPath fi
|
||||||
|
|
||||||
go (Modified { isDirectory = isd, maybeFilePath = Just fi })
|
go (Modified { isDirectory = isd, maybeFilePath = Just fi })
|
||||||
| isd = noop
|
| isd = noop
|
||||||
| otherwise = runhook modifyHook (fromInternalFilePath fi) Nothing
|
| otherwise = runhook modifyHook (toOsPath fi) Nothing
|
||||||
|
|
||||||
go _ = noop
|
go _ = noop
|
||||||
|
|
||||||
|
@ -150,35 +149,36 @@ watchDir i dir ignored scanevents hooks
|
||||||
|
|
||||||
indir f = dir </> f
|
indir f = dir </> f
|
||||||
|
|
||||||
getstatus f = catchMaybeIO $ R.getSymbolicLinkStatus $ toRawFilePath $ indir f
|
getstatus f = catchMaybeIO $ R.getSymbolicLinkStatus $ fromOsPath $ indir f
|
||||||
|
|
||||||
checkfiletype check h f = do
|
checkfiletype check h f = do
|
||||||
ms <- getstatus f
|
ms <- getstatus f
|
||||||
case ms of
|
case ms of
|
||||||
Just s
|
Just s
|
||||||
| check s -> runhook h f ms
|
| check s -> runhook h f ms
|
||||||
_ -> noop
|
_ -> noop
|
||||||
filetype t f = catchBoolIO $ t <$> R.getSymbolicLinkStatus (toRawFilePath (indir f))
|
filetype t f = catchBoolIO $ t <$> R.getSymbolicLinkStatus (fromOsPath (indir f))
|
||||||
|
|
||||||
failedaddwatch e
|
failedaddwatch e
|
||||||
-- Inotify fails when there are too many watches with a
|
-- Inotify fails when there are too many watches with a
|
||||||
-- disk full error.
|
-- disk full error.
|
||||||
| isFullError e =
|
| isFullError e =
|
||||||
case errHook hooks of
|
case errHook hooks of
|
||||||
Nothing -> giveup $ "failed to add inotify watch on directory " ++ dir ++ " (" ++ show e ++ ")"
|
Nothing -> giveup $ "failed to add inotify watch on directory " ++ fromOsPath dir ++ " (" ++ show e ++ ")"
|
||||||
Just hook -> tooManyWatches hook dir
|
Just hook -> tooManyWatches hook dir
|
||||||
-- The directory could have been deleted.
|
-- The directory could have been deleted.
|
||||||
| isDoesNotExistError e = return ()
|
| isDoesNotExistError e = return ()
|
||||||
| otherwise = throw e
|
| otherwise = throw e
|
||||||
|
|
||||||
failedwatch e = hPutStrLn stderr $ "failed to add watch on directory " ++ dir ++ " (" ++ show e ++ ")"
|
failedwatch e = hPutStrLn stderr $ "failed to add watch on directory " ++ fromOsPath dir ++ " (" ++ show e ++ ")"
|
||||||
|
|
||||||
tooManyWatches :: (String -> Maybe FileStatus -> IO ()) -> FilePath -> IO ()
|
tooManyWatches :: (String -> Maybe FileStatus -> IO ()) -> OsPath -> IO ()
|
||||||
tooManyWatches hook dir = do
|
tooManyWatches hook dir = do
|
||||||
sysctlval <- querySysctl [Param maxwatches] :: IO (Maybe Integer)
|
sysctlval <- querySysctl [Param maxwatches] :: IO (Maybe Integer)
|
||||||
hook (unlines $ basewarning : maybe withoutsysctl withsysctl sysctlval) Nothing
|
hook (unlines $ basewarning : maybe withoutsysctl withsysctl sysctlval) Nothing
|
||||||
where
|
where
|
||||||
maxwatches = "fs.inotify.max_user_watches"
|
maxwatches = "fs.inotify.max_user_watches"
|
||||||
basewarning = "Too many directories to watch! (Not watching " ++ dir ++")"
|
basewarning = "Too many directories to watch! (Not watching " ++ fromOsPath dir ++")"
|
||||||
withoutsysctl = ["Increase the value in /proc/sys/fs/inotify/max_user_watches"]
|
withoutsysctl = ["Increase the value in /proc/sys/fs/inotify/max_user_watches"]
|
||||||
withsysctl n = let new = n * 10 in
|
withsysctl n = let new = n * 10 in
|
||||||
[ "Increase the limit permanently by running:"
|
[ "Increase the limit permanently by running:"
|
||||||
|
@ -197,9 +197,3 @@ querySysctl ps = getM go ["sysctl", "/sbin/sysctl", "/usr/sbin/sysctl"]
|
||||||
Nothing -> return Nothing
|
Nothing -> return Nothing
|
||||||
Just s -> return $ parsesysctl s
|
Just s -> return $ parsesysctl s
|
||||||
parsesysctl s = readish =<< lastMaybe (words s)
|
parsesysctl s = readish =<< lastMaybe (words s)
|
||||||
|
|
||||||
toInternalFilePath :: FilePath -> RawFilePath
|
|
||||||
toInternalFilePath = toRawFilePath
|
|
||||||
|
|
||||||
fromInternalFilePath :: RawFilePath -> FilePath
|
|
||||||
fromInternalFilePath = fromRawFilePath
|
|
||||||
|
|
|
@ -16,12 +16,12 @@ import Common
|
||||||
type Hook a = Maybe (a -> Maybe FileStatus -> IO ())
|
type Hook a = Maybe (a -> Maybe FileStatus -> IO ())
|
||||||
|
|
||||||
data WatchHooks = WatchHooks
|
data WatchHooks = WatchHooks
|
||||||
{ addHook :: Hook FilePath
|
{ addHook :: Hook OsPath
|
||||||
, addSymlinkHook :: Hook FilePath
|
, addSymlinkHook :: Hook OsPath
|
||||||
, delHook :: Hook FilePath
|
, delHook :: Hook OsPath
|
||||||
, delDirHook :: Hook FilePath
|
, delDirHook :: Hook OsPath
|
||||||
, errHook :: Hook String -- error message
|
, errHook :: Hook String -- error message
|
||||||
, modifyHook :: Hook FilePath
|
, modifyHook :: Hook OsPath
|
||||||
}
|
}
|
||||||
|
|
||||||
mkWatchHooks :: WatchHooks
|
mkWatchHooks :: WatchHooks
|
||||||
|
|
|
@ -25,9 +25,7 @@ import Prelude
|
||||||
import Utility.SystemDirectory
|
import Utility.SystemDirectory
|
||||||
import Utility.Path.AbsRel
|
import Utility.Path.AbsRel
|
||||||
import Utility.Exception
|
import Utility.Exception
|
||||||
import Utility.FileSystemEncoding
|
|
||||||
import Utility.OsPath
|
import Utility.OsPath
|
||||||
import qualified Utility.RawFilePath as R
|
|
||||||
import Utility.PartialPrelude
|
import Utility.PartialPrelude
|
||||||
|
|
||||||
{- Like createDirectoryIfMissing True, but it will only create
|
{- Like createDirectoryIfMissing True, but it will only create
|
||||||
|
@ -69,7 +67,7 @@ createDirectoryUnder' topdirs dir0 mkdir = do
|
||||||
-- it's not. And on Windows, if they are on different drives,
|
-- it's not. And on Windows, if they are on different drives,
|
||||||
-- the path will not be relative.
|
-- the path will not be relative.
|
||||||
let notbeneath = \(_topdir, (relp, dirs)) ->
|
let notbeneath = \(_topdir, (relp, dirs)) ->
|
||||||
headMaybe dirs /= Just ".." && not (isAbsolute relp)
|
headMaybe dirs /= Just (literalOsPath "..") && not (isAbsolute relp)
|
||||||
case filter notbeneath $ zip topdirs (zip relps relparts) of
|
case filter notbeneath $ zip topdirs (zip relps relparts) of
|
||||||
((topdir, (_relp, dirs)):_)
|
((topdir, (_relp, dirs)):_)
|
||||||
-- If dir0 is the same as the topdir, don't try to
|
-- If dir0 is the same as the topdir, don't try to
|
||||||
|
|
|
@ -416,9 +416,9 @@ testHarness tmpdir cmd a = ifM (inSearchPath (unGpgCmd cmd))
|
||||||
setup = do
|
setup = do
|
||||||
subdir <- makenewdir (1 :: Integer)
|
subdir <- makenewdir (1 :: Integer)
|
||||||
origenviron <- getEnvironment
|
origenviron <- getEnvironment
|
||||||
let environ = addEntry var subdir origenviron
|
let environ = addEntry var (fromOsPath subdir) origenviron
|
||||||
-- gpg is picky about permissions on its home dir
|
-- gpg is picky about permissions on its home dir
|
||||||
liftIO $ void $ tryIO $ modifyFileMode (toRawFilePath subdir) $
|
liftIO $ void $ tryIO $ modifyFileMode (fromOsPath subdir) $
|
||||||
removeModes $ otherGroupModes
|
removeModes $ otherGroupModes
|
||||||
-- For some reason, recent gpg needs a trustdb to be set up.
|
-- For some reason, recent gpg needs a trustdb to be set up.
|
||||||
_ <- pipeStrict' cmd [Param "--trust-model", Param "auto", Param "--update-trustdb"] (Just environ) mempty
|
_ <- pipeStrict' cmd [Param "--trust-model", Param "auto", Param "--update-trustdb"] (Just environ) mempty
|
||||||
|
|
|
@ -44,7 +44,7 @@ installLib installfile top lib = ifM (doesFileExist (toOsPath lib))
|
||||||
( do
|
( do
|
||||||
installfile top lib
|
installfile top lib
|
||||||
checksymlink lib
|
checksymlink lib
|
||||||
return $ Just $ fromRawFilePath $ parentDir $ toRawFilePath lib
|
return $ Just $ fromOsPath $ parentDir $ toOsPath lib
|
||||||
, return Nothing
|
, return Nothing
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
|
@ -50,7 +50,6 @@ import System.Posix.Files.ByteString
|
||||||
import System.Posix.Process
|
import System.Posix.Process
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.IO.Class (liftIO, MonadIO)
|
import Control.Monad.IO.Class (liftIO, MonadIO)
|
||||||
import qualified System.FilePath.ByteString as P
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.List
|
import Data.List
|
||||||
import Network.BSD
|
import Network.BSD
|
||||||
|
@ -151,7 +150,7 @@ tryLock lockfile = do
|
||||||
where
|
where
|
||||||
go abslockfile sidelock = do
|
go abslockfile sidelock = do
|
||||||
(tmp, h) <- openTmpFileIn
|
(tmp, h) <- openTmpFileIn
|
||||||
(toOsPath (P.takeDirectory abslockfile))
|
(takeDirectory abslockfile)
|
||||||
(literalOsPath "locktmp")
|
(literalOsPath "locktmp")
|
||||||
let tmp' = fromOsPath tmp
|
let tmp' = fromOsPath tmp
|
||||||
setFileMode tmp' (combineModes readModes)
|
setFileMode tmp' (combineModes readModes)
|
||||||
|
@ -162,7 +161,7 @@ tryLock lockfile = do
|
||||||
removeWhenExistsWith removeLink tmp'
|
removeWhenExistsWith removeLink tmp'
|
||||||
return Nothing
|
return Nothing
|
||||||
let tooklock st = return $ Just $ LockHandle abslockfile st sidelock
|
let tooklock st = return $ Just $ LockHandle abslockfile st sidelock
|
||||||
linkToLock sidelock tmp' abslockfile >>= \case
|
linkToLock sidelock tmp' (fromOsPath abslockfile) >>= \case
|
||||||
Just lckst -> do
|
Just lckst -> do
|
||||||
removeWhenExistsWith removeLink tmp'
|
removeWhenExistsWith removeLink tmp'
|
||||||
tooklock lckst
|
tooklock lckst
|
||||||
|
@ -177,7 +176,7 @@ tryLock lockfile = do
|
||||||
-- the pidlock was taken on,
|
-- the pidlock was taken on,
|
||||||
-- we know that the pidlock is
|
-- we know that the pidlock is
|
||||||
-- stale, and can take it over.
|
-- stale, and can take it over.
|
||||||
rename tmp' abslockfile
|
rename tmp' (fromOsPath abslockfile)
|
||||||
tooklock tmpst
|
tooklock tmpst
|
||||||
_ -> failedlock
|
_ -> failedlock
|
||||||
|
|
||||||
|
@ -201,7 +200,7 @@ linkToLock (Just _) src dest = do
|
||||||
Right _ -> do
|
Right _ -> do
|
||||||
_ <- tryIO $ createLink src dest
|
_ <- tryIO $ createLink src dest
|
||||||
ifM (catchBoolIO checklinked)
|
ifM (catchBoolIO checklinked)
|
||||||
( ifM (catchBoolIO $ not <$> checkInsaneLustre dest)
|
( ifM (catchBoolIO $ not <$> checkInsaneLustre (toOsPath dest))
|
||||||
( catchMaybeIO $ getFileStatus dest
|
( catchMaybeIO $ getFileStatus dest
|
||||||
, return Nothing
|
, return Nothing
|
||||||
)
|
)
|
||||||
|
@ -243,16 +242,16 @@ linkToLock (Just _) src dest = do
|
||||||
-- We can detect this insanity by getting the directory contents after
|
-- We can detect this insanity by getting the directory contents after
|
||||||
-- making the link, and checking to see if 2 copies of the dest file,
|
-- making the link, and checking to see if 2 copies of the dest file,
|
||||||
-- with the SAME FILENAME exist.
|
-- with the SAME FILENAME exist.
|
||||||
checkInsaneLustre :: RawFilePath -> IO Bool
|
checkInsaneLustre :: OsPath -> IO Bool
|
||||||
checkInsaneLustre dest = do
|
checkInsaneLustre dest = do
|
||||||
fs <- dirContents (P.takeDirectory dest)
|
fs <- dirContents (takeDirectory dest)
|
||||||
case length (filter (== dest) fs) of
|
case length (filter (== dest) fs) of
|
||||||
1 -> return False -- whew!
|
1 -> return False -- whew!
|
||||||
0 -> return True -- wtf?
|
0 -> return True -- wtf?
|
||||||
_ -> do
|
_ -> do
|
||||||
-- Try to clean up the extra copy we made
|
-- Try to clean up the extra copy we made
|
||||||
-- that has the same name. Egads.
|
-- that has the same name. Egads.
|
||||||
_ <- tryIO $ removeLink dest
|
_ <- tryIO $ removeLink $ fromOsPath dest
|
||||||
return True
|
return True
|
||||||
|
|
||||||
-- | Waits as necessary to take a lock.
|
-- | Waits as necessary to take a lock.
|
||||||
|
@ -268,7 +267,7 @@ waitLock (Seconds timeout) lockfile displaymessage sem = go timeout
|
||||||
| n > 0 = liftIO (tryLock lockfile) >>= \case
|
| n > 0 = liftIO (tryLock lockfile) >>= \case
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
when (n == pred timeout) $
|
when (n == pred timeout) $
|
||||||
displaymessage $ "waiting for pid lock file " ++ fromRawFilePath lockfile ++ " which is held by another process (or may be stale)"
|
displaymessage $ "waiting for pid lock file " ++ fromOsPath lockfile ++ " which is held by another process (or may be stale)"
|
||||||
liftIO $ threadDelaySeconds (Seconds 1)
|
liftIO $ threadDelaySeconds (Seconds 1)
|
||||||
go (pred n)
|
go (pred n)
|
||||||
Just lckh -> do
|
Just lckh -> do
|
||||||
|
@ -280,15 +279,15 @@ waitLock (Seconds timeout) lockfile displaymessage sem = go timeout
|
||||||
|
|
||||||
waitedLock :: MonadIO m => Seconds -> PidLockFile -> (String -> m ()) -> m a
|
waitedLock :: MonadIO m => Seconds -> PidLockFile -> (String -> m ()) -> m a
|
||||||
waitedLock (Seconds timeout) lockfile displaymessage = do
|
waitedLock (Seconds timeout) lockfile displaymessage = do
|
||||||
displaymessage $ show timeout ++ " second timeout exceeded while waiting for pid lock file " ++ fromRawFilePath lockfile
|
displaymessage $ show timeout ++ " second timeout exceeded while waiting for pid lock file " ++ fromOsPath lockfile
|
||||||
giveup $ "Gave up waiting for pid lock file " ++ fromRawFilePath lockfile
|
giveup $ "Gave up waiting for pid lock file " ++ fromOsPath lockfile
|
||||||
|
|
||||||
-- | Use when the pid lock has already been taken by another thread of the
|
-- | Use when the pid lock has already been taken by another thread of the
|
||||||
-- same process.
|
-- same process.
|
||||||
alreadyLocked :: MonadIO m => PidLockFile -> m LockHandle
|
alreadyLocked :: MonadIO m => PidLockFile -> m LockHandle
|
||||||
alreadyLocked lockfile = liftIO $ do
|
alreadyLocked lockfile = liftIO $ do
|
||||||
abslockfile <- absPath lockfile
|
abslockfile <- absPath lockfile
|
||||||
st <- getFileStatus abslockfile
|
st <- getFileStatus (fromOsPath abslockfile)
|
||||||
return $ LockHandle abslockfile st Nothing
|
return $ LockHandle abslockfile st Nothing
|
||||||
|
|
||||||
dropLock :: LockHandle -> IO ()
|
dropLock :: LockHandle -> IO ()
|
||||||
|
@ -296,7 +295,7 @@ dropLock (LockHandle lockfile _ sidelock) = do
|
||||||
-- Drop side lock first, at which point the pid lock will be
|
-- Drop side lock first, at which point the pid lock will be
|
||||||
-- considered stale.
|
-- considered stale.
|
||||||
dropSideLock sidelock
|
dropSideLock sidelock
|
||||||
removeWhenExistsWith removeLink lockfile
|
removeWhenExistsWith removeLink (fromOsPath lockfile)
|
||||||
dropLock ParentLocked = return ()
|
dropLock ParentLocked = return ()
|
||||||
|
|
||||||
getLockStatus :: PidLockFile -> IO LockStatus
|
getLockStatus :: PidLockFile -> IO LockStatus
|
||||||
|
@ -312,7 +311,7 @@ checkLocked lockfile = conv <$> getLockStatus lockfile
|
||||||
-- locked to get the LockHandle.
|
-- locked to get the LockHandle.
|
||||||
checkSaneLock :: PidLockFile -> LockHandle -> IO Bool
|
checkSaneLock :: PidLockFile -> LockHandle -> IO Bool
|
||||||
checkSaneLock lockfile (LockHandle _ st _) =
|
checkSaneLock lockfile (LockHandle _ st _) =
|
||||||
go =<< catchMaybeIO (getFileStatus lockfile)
|
go =<< catchMaybeIO (getFileStatus (fromOsPath lockfile))
|
||||||
where
|
where
|
||||||
go Nothing = return False
|
go Nothing = return False
|
||||||
go (Just st') = return $
|
go (Just st') = return $
|
||||||
|
@ -327,9 +326,9 @@ checkSaneLock _ ParentLocked = return True
|
||||||
-- The parent process should keep running as long as the child
|
-- The parent process should keep running as long as the child
|
||||||
-- process is running, since the child inherits the environment and will
|
-- process is running, since the child inherits the environment and will
|
||||||
-- not see unsetLockEnv.
|
-- not see unsetLockEnv.
|
||||||
pidLockEnv :: RawFilePath -> IO String
|
pidLockEnv :: OsPath -> IO String
|
||||||
pidLockEnv lockfile = do
|
pidLockEnv lockfile = do
|
||||||
abslockfile <- fromRawFilePath <$> absPath lockfile
|
abslockfile <- fromOsPath <$> absPath lockfile
|
||||||
return $ "PIDLOCK_" ++ filter legalInEnvVar abslockfile
|
return $ "PIDLOCK_" ++ filter legalInEnvVar abslockfile
|
||||||
|
|
||||||
pidLockEnvValue :: String
|
pidLockEnvValue :: String
|
||||||
|
|
|
@ -25,6 +25,7 @@ import Utility.Applicative
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
import Utility.LockFile.LockStatus
|
import Utility.LockFile.LockStatus
|
||||||
import Utility.OpenFd
|
import Utility.OpenFd
|
||||||
|
import Utility.OsPath
|
||||||
|
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Posix.Types
|
import System.Posix.Types
|
||||||
|
@ -33,7 +34,7 @@ import System.Posix.Files.ByteString
|
||||||
import System.FilePath.ByteString (RawFilePath)
|
import System.FilePath.ByteString (RawFilePath)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
|
||||||
type LockFile = RawFilePath
|
type LockFile = OsPath
|
||||||
|
|
||||||
newtype LockHandle = LockHandle Fd
|
newtype LockHandle = LockHandle Fd
|
||||||
|
|
||||||
|
@ -75,11 +76,12 @@ tryLock lockreq mode lockfile = uninterruptibleMask_ $ do
|
||||||
-- Close on exec flag is set so child processes do not inherit the lock.
|
-- Close on exec flag is set so child processes do not inherit the lock.
|
||||||
openLockFile :: LockRequest -> Maybe ModeSetter -> LockFile -> IO Fd
|
openLockFile :: LockRequest -> Maybe ModeSetter -> LockFile -> IO Fd
|
||||||
openLockFile lockreq filemode lockfile = do
|
openLockFile lockreq filemode lockfile = do
|
||||||
l <- applyModeSetter filemode lockfile $ \filemode' ->
|
l <- applyModeSetter filemode lockfile' $ \filemode' ->
|
||||||
openFdWithMode lockfile openfor filemode' defaultFileFlags
|
openFdWithMode lockfile' openfor filemode' defaultFileFlags
|
||||||
setFdOption l CloseOnExec True
|
setFdOption l CloseOnExec True
|
||||||
return l
|
return l
|
||||||
where
|
where
|
||||||
|
lockfile' = fromOsPath lockfile
|
||||||
openfor = case lockreq of
|
openfor = case lockreq of
|
||||||
ReadLock -> ReadOnly
|
ReadLock -> ReadOnly
|
||||||
_ -> ReadWrite
|
_ -> ReadWrite
|
||||||
|
@ -120,7 +122,7 @@ dropLock (LockHandle fd) = closeFd fd
|
||||||
-- else.
|
-- else.
|
||||||
checkSaneLock :: LockFile -> LockHandle -> IO Bool
|
checkSaneLock :: LockFile -> LockHandle -> IO Bool
|
||||||
checkSaneLock lockfile (LockHandle fd) =
|
checkSaneLock lockfile (LockHandle fd) =
|
||||||
go =<< catchMaybeIO (getFileStatus lockfile)
|
go =<< catchMaybeIO (getFileStatus (fromOsPath lockfile))
|
||||||
where
|
where
|
||||||
go Nothing = return False
|
go Nothing = return False
|
||||||
go (Just st) = do
|
go (Just st) = do
|
||||||
|
|
|
@ -23,14 +23,14 @@ module Utility.LockPool.STM (
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Utility.Monad
|
import Utility.Monad
|
||||||
|
import Utility.OsPath
|
||||||
|
|
||||||
import System.IO.Unsafe (unsafePerformIO)
|
import System.IO.Unsafe (unsafePerformIO)
|
||||||
import System.FilePath.ByteString (RawFilePath)
|
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
|
|
||||||
type LockFile = RawFilePath
|
type LockFile = OsPath
|
||||||
|
|
||||||
data LockMode = LockExclusive | LockShared
|
data LockMode = LockExclusive | LockShared
|
||||||
deriving (Eq)
|
deriving (Eq)
|
||||||
|
|
|
@ -17,41 +17,39 @@ module Utility.Path.Tests (
|
||||||
prop_dirContains_regressionTest,
|
prop_dirContains_regressionTest,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Char
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
import Common
|
import Common
|
||||||
import Utility.Path
|
|
||||||
import Utility.QuickCheck
|
import Utility.QuickCheck
|
||||||
|
import qualified Utility.OsString as OS
|
||||||
|
|
||||||
prop_upFrom_basics :: TestableFilePath -> Bool
|
prop_upFrom_basics :: TestableFilePath -> Bool
|
||||||
prop_upFrom_basics tdir
|
prop_upFrom_basics tdir
|
||||||
| dir == "/" = p == Nothing
|
| dir == "/" = p == Nothing
|
||||||
| otherwise = p /= Just dir
|
| otherwise = p /= Just dir
|
||||||
where
|
where
|
||||||
p = fromRawFilePath <$> upFrom (toRawFilePath dir)
|
p = fromOsPath <$> upFrom (toOsPath dir)
|
||||||
dir = fromTestableFilePath tdir
|
dir = fromTestableFilePath tdir
|
||||||
|
|
||||||
prop_relPathDirToFileAbs_basics :: TestableFilePath -> Bool
|
prop_relPathDirToFileAbs_basics :: TestableFilePath -> Bool
|
||||||
prop_relPathDirToFileAbs_basics pt = and
|
prop_relPathDirToFileAbs_basics pt = and
|
||||||
[ relPathDirToFileAbs p (p </> "bar") == "bar"
|
[ relPathDirToFileAbs p (p </> literalOsPath "bar") == literalOsPath "bar"
|
||||||
, relPathDirToFileAbs (p </> "bar") p == ".."
|
, relPathDirToFileAbs (p </> literalOsPath "bar") p == literalOsPath ".."
|
||||||
, relPathDirToFileAbs p p == ""
|
, relPathDirToFileAbs p p == literalOsPath ""
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
-- relPathDirToFileAbs needs absolute paths, so make the path
|
-- relPathDirToFileAbs needs absolute paths, so make the path
|
||||||
-- absolute by adding a path separator to the front.
|
-- absolute by adding a path separator to the front.
|
||||||
p = pathSeparator `B.cons` relf
|
p = pathSeparator `OS.cons` relf
|
||||||
-- Make the input a relative path. On windows, make sure it does
|
-- Make the input a relative path. On windows, make sure it does
|
||||||
-- not contain anything that looks like a drive letter.
|
-- not contain anything that looks like a drive letter.
|
||||||
relf = B.dropWhile isPathSeparator $
|
relf = OS.dropWhile isPathSeparator $
|
||||||
B.filter (not . skipchar) $
|
OS.filter (not . skipchar) $
|
||||||
toRawFilePath (fromTestableFilePath pt)
|
toOsPath (fromTestableFilePath pt)
|
||||||
skipchar b = b == (fromIntegral (ord ':'))
|
skipchar b = b == unsafeFromChar ':'
|
||||||
|
|
||||||
prop_relPathDirToFileAbs_regressionTest :: Bool
|
prop_relPathDirToFileAbs_regressionTest :: Bool
|
||||||
prop_relPathDirToFileAbs_regressionTest = same_dir_shortcurcuits_at_difference
|
prop_relPathDirToFileAbs_regressionTest = same_dir_shortcurcuits_at_difference
|
||||||
|
@ -60,21 +58,25 @@ prop_relPathDirToFileAbs_regressionTest = same_dir_shortcurcuits_at_difference
|
||||||
- location, but it's not really the same directory.
|
- location, but it's not really the same directory.
|
||||||
- Code used to get this wrong. -}
|
- Code used to get this wrong. -}
|
||||||
same_dir_shortcurcuits_at_difference =
|
same_dir_shortcurcuits_at_difference =
|
||||||
relPathDirToFileAbs (joinPath [pathSeparator `B.cons` "tmp", "r", "lll", "xxx", "yyy", "18"])
|
relPathDirToFileAbs (mkp [fromOsPath (pathSeparator `OS.cons` literalOsPath "tmp"), "r", "lll", "xxx", "yyy", "18"])
|
||||||
(joinPath [pathSeparator `B.cons` "tmp", "r", ".git", "annex", "objects", "18", "gk", "SHA256-foo", "SHA256-foo"])
|
(mkp [fromOsPath (pathSeparator `OS.cons` literalOsPath "tmp"), "r", ".git", "annex", "objects", "18", "gk", "SHA256-foo", "SHA256-foo"])
|
||||||
== joinPath ["..", "..", "..", "..", ".git", "annex", "objects", "18", "gk", "SHA256-foo", "SHA256-foo"]
|
== mkp ["..", "..", "..", "..", ".git", "annex", "objects", "18", "gk", "SHA256-foo", "SHA256-foo"]
|
||||||
|
where
|
||||||
|
mkp = joinPath . map literalOsPath
|
||||||
|
|
||||||
prop_dirContains_regressionTest :: Bool
|
prop_dirContains_regressionTest :: Bool
|
||||||
prop_dirContains_regressionTest = and
|
prop_dirContains_regressionTest = and
|
||||||
[ not $ dirContains "." ".."
|
[ not $ dc "." ".."
|
||||||
, not $ dirContains ".." "../.."
|
, not $ dc ".." "../.."
|
||||||
, dirContains "." "foo"
|
, dc "." "foo"
|
||||||
, dirContains "." "."
|
, dc "." "."
|
||||||
, dirContains ".." ".."
|
, dc ".." ".."
|
||||||
, dirContains "../.." "../.."
|
, dc "../.." "../.."
|
||||||
, dirContains "." "./foo"
|
, dc "." "./foo"
|
||||||
, dirContains ".." "../foo"
|
, dc ".." "../foo"
|
||||||
, dirContains "../.." "../foo"
|
, dc "../.." "../foo"
|
||||||
, dirContains "../.." "../../foo"
|
, dc "../.." "../../foo"
|
||||||
, not $ dirContains "../.." "../../.."
|
, not $ dc "../.." "../../.."
|
||||||
]
|
]
|
||||||
|
where
|
||||||
|
dc x y = dirContains (literalOsPath x) (literalOsPath y)
|
||||||
|
|
|
@ -14,11 +14,10 @@ module Utility.Path.Windows (
|
||||||
|
|
||||||
import Utility.Path
|
import Utility.Path
|
||||||
import Utility.OsPath
|
import Utility.OsPath
|
||||||
import Utility.FileSystemEncoding
|
import Utility.SystemDirectory
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified System.FilePath.Windows.ByteString as P
|
import qualified System.FilePath.Windows.ByteString as P
|
||||||
import System.Directory (getCurrentDirectory)
|
|
||||||
|
|
||||||
{- Convert a filepath to use Windows's native namespace.
|
{- Convert a filepath to use Windows's native namespace.
|
||||||
- This avoids filesystem length limits.
|
- This avoids filesystem length limits.
|
||||||
|
@ -36,8 +35,8 @@ convertToWindowsNativeNamespace f
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
-- Make absolute because any '.' and '..' in the path
|
-- Make absolute because any '.' and '..' in the path
|
||||||
-- will not be resolved once it's converted.
|
-- will not be resolved once it's converted.
|
||||||
cwd <- toRawFilePath <$> getCurrentDirectory
|
cwd <- getCurrentDirectory
|
||||||
let p = fromOsPath (simplifyPath (toOsPath (combine cwd f)))
|
let p = fromOsPath (simplifyPath (combine cwd (toOsPath f)))
|
||||||
-- Normalize slashes.
|
-- Normalize slashes.
|
||||||
let p' = P.normalise p
|
let p' = P.normalise p
|
||||||
return (win32_file_namespace <> p')
|
return (win32_file_namespace <> p')
|
||||||
|
|
|
@ -134,16 +134,16 @@ modifyUserSshConfig modifier = changeUserSshConfig $
|
||||||
changeUserSshConfig :: (String -> String) -> IO ()
|
changeUserSshConfig :: (String -> String) -> IO ()
|
||||||
changeUserSshConfig modifier = do
|
changeUserSshConfig modifier = do
|
||||||
sshdir <- sshDir
|
sshdir <- sshDir
|
||||||
let configfile = sshdir </> "config"
|
let configfile = sshdir </> literalOsPath "config"
|
||||||
whenM (doesFileExist configfile) $ do
|
whenM (doesFileExist configfile) $ do
|
||||||
c <- decodeBS . S8.unlines . fileLines'
|
c <- decodeBS . S8.unlines . fileLines'
|
||||||
<$> F.readFile' (toOsPath (toRawFilePath configfile))
|
<$> F.readFile' configfile
|
||||||
let c' = modifier c
|
let c' = modifier c
|
||||||
when (c /= c') $ do
|
when (c /= c') $ do
|
||||||
-- If it's a symlink, replace the file it
|
-- If it's a symlink, replace the file it
|
||||||
-- points to.
|
-- points to.
|
||||||
f <- catchDefaultIO configfile (canonicalizePath configfile)
|
f <- catchDefaultIO configfile (canonicalizePath configfile)
|
||||||
viaTmp writeSshConfig (toOsPath (toRawFilePath f)) c'
|
viaTmp writeSshConfig f c'
|
||||||
|
|
||||||
writeSshConfig :: OsPath -> String -> IO ()
|
writeSshConfig :: OsPath -> String -> IO ()
|
||||||
writeSshConfig f s = do
|
writeSshConfig f s = do
|
||||||
|
@ -161,7 +161,7 @@ setSshConfigMode :: RawFilePath -> IO ()
|
||||||
setSshConfigMode f = void $ tryIO $ modifyFileMode f $
|
setSshConfigMode f = void $ tryIO $ modifyFileMode f $
|
||||||
removeModes [groupWriteMode, otherWriteMode]
|
removeModes [groupWriteMode, otherWriteMode]
|
||||||
|
|
||||||
sshDir :: IO FilePath
|
sshDir :: IO OsPath
|
||||||
sshDir = do
|
sshDir = do
|
||||||
home <- myHomeDir
|
home <- myHomeDir
|
||||||
return $ home </> ".ssh"
|
return $ toOsPath home </> literalOsPath ".ssh"
|
||||||
|
|
|
@ -70,7 +70,7 @@ newtype Armoring = Armoring Bool
|
||||||
- The directory does not really have to be empty, it just needs to be one
|
- The directory does not really have to be empty, it just needs to be one
|
||||||
- that should not contain any files with names starting with "@".
|
- that should not contain any files with names starting with "@".
|
||||||
-}
|
-}
|
||||||
newtype EmptyDirectory = EmptyDirectory FilePath
|
newtype EmptyDirectory = EmptyDirectory OsPath
|
||||||
|
|
||||||
{- Encrypt using symmetric encryption with the specified password. -}
|
{- Encrypt using symmetric encryption with the specified password. -}
|
||||||
encryptSymmetric
|
encryptSymmetric
|
||||||
|
@ -112,7 +112,7 @@ decryptSymmetric sopcmd password emptydirectory feeder reader =
|
||||||
{- Test a value round-trips through symmetric encryption and decryption. -}
|
{- Test a value round-trips through symmetric encryption and decryption. -}
|
||||||
test_encrypt_decrypt_Symmetric :: SOPCmd -> SOPCmd -> Password -> Armoring -> B.ByteString -> IO Bool
|
test_encrypt_decrypt_Symmetric :: SOPCmd -> SOPCmd -> Password -> Armoring -> B.ByteString -> IO Bool
|
||||||
test_encrypt_decrypt_Symmetric a b password armoring v = catchBoolIO $
|
test_encrypt_decrypt_Symmetric a b password armoring v = catchBoolIO $
|
||||||
withTmpDir (toOsPath "test") $ \d -> do
|
withTmpDir (literalOsPath "test") $ \d -> do
|
||||||
let ed = EmptyDirectory d
|
let ed = EmptyDirectory d
|
||||||
enc <- encryptSymmetric a password ed Nothing armoring
|
enc <- encryptSymmetric a password ed Nothing armoring
|
||||||
(`B.hPutStr` v) B.hGetContents
|
(`B.hPutStr` v) B.hGetContents
|
||||||
|
@ -188,7 +188,7 @@ feedRead' (SOPCmd cmd) subcmd params med feeder reader = do
|
||||||
, std_out = CreatePipe
|
, std_out = CreatePipe
|
||||||
, std_err = Inherit
|
, std_err = Inherit
|
||||||
, cwd = case med of
|
, cwd = case med of
|
||||||
Just (EmptyDirectory d) -> Just d
|
Just (EmptyDirectory d) -> Just (fromOsPath d)
|
||||||
Nothing -> Nothing
|
Nothing -> Nothing
|
||||||
}
|
}
|
||||||
copyright =<< bracket (setup p) cleanup (go p)
|
copyright =<< bracket (setup p) cleanup (go p)
|
||||||
|
|
|
@ -70,7 +70,7 @@ runSuCommand Nothing _ = return False
|
||||||
mkSuCommand :: String -> [CommandParam] -> IO (Maybe SuCommand)
|
mkSuCommand :: String -> [CommandParam] -> IO (Maybe SuCommand)
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
mkSuCommand cmd ps = do
|
mkSuCommand cmd ps = do
|
||||||
pwd <- getCurrentDirectory
|
pwd <- fromOsPath <$> getCurrentDirectory
|
||||||
firstM (\(SuCommand _ p _) -> inSearchPath p) =<< selectcmds pwd
|
firstM (\(SuCommand _ p _) -> inSearchPath p) =<< selectcmds pwd
|
||||||
where
|
where
|
||||||
selectcmds pwd = ifM (inx <||> (not <$> atconsole))
|
selectcmds pwd = ifM (inx <||> (not <$> atconsole))
|
||||||
|
|
|
@ -21,6 +21,7 @@ import Common
|
||||||
import Utility.ThreadScheduler
|
import Utility.ThreadScheduler
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
import Utility.RawFilePath (setOwnerAndGroup)
|
import Utility.RawFilePath (setOwnerAndGroup)
|
||||||
|
import qualified Utility.OsString as OS
|
||||||
|
|
||||||
import System.PosixCompat.Types
|
import System.PosixCompat.Types
|
||||||
import System.PosixCompat.Files (ownerReadMode, ownerWriteMode, ownerExecuteMode)
|
import System.PosixCompat.Files (ownerReadMode, ownerWriteMode, ownerExecuteMode)
|
||||||
|
@ -35,7 +36,7 @@ type OnionPort = Int
|
||||||
newtype OnionAddress = OnionAddress String
|
newtype OnionAddress = OnionAddress String
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
type OnionSocket = FilePath
|
type OnionSocket = OsPath
|
||||||
|
|
||||||
-- | A unique identifier for a hidden service.
|
-- | A unique identifier for a hidden service.
|
||||||
type UniqueIdent = String
|
type UniqueIdent = String
|
||||||
|
@ -68,21 +69,21 @@ connectHiddenService (OnionAddress address) port = do
|
||||||
addHiddenService :: AppName -> UserID -> UniqueIdent -> IO (OnionAddress, OnionPort)
|
addHiddenService :: AppName -> UserID -> UniqueIdent -> IO (OnionAddress, OnionPort)
|
||||||
addHiddenService appname uid ident = do
|
addHiddenService appname uid ident = do
|
||||||
prepHiddenServiceSocketDir appname uid ident
|
prepHiddenServiceSocketDir appname uid ident
|
||||||
ls <- lines <$> (readFile =<< findTorrc)
|
ls <- lines <$> (readFile . fromOsPath =<< findTorrc)
|
||||||
let portssocks = mapMaybe (parseportsock . separate isSpace) ls
|
let portssocks = mapMaybe (parseportsock . separate isSpace) ls
|
||||||
case filter (\(_, s) -> s == sockfile) portssocks of
|
case filter (\(_, s) -> s == fromOsPath sockfile) portssocks of
|
||||||
((p, _s):_) -> waithiddenservice 1 p
|
((p, _s):_) -> waithiddenservice 1 p
|
||||||
_ -> do
|
_ -> do
|
||||||
highports <- R.getStdRandom mkhighports
|
highports <- R.getStdRandom mkhighports
|
||||||
let newport = fromMaybe (error "internal") $ headMaybe $
|
let newport = fromMaybe (error "internal") $ headMaybe $
|
||||||
filter (`notElem` map fst portssocks) highports
|
filter (`notElem` map fst portssocks) highports
|
||||||
torrc <- findTorrc
|
torrc <- findTorrc
|
||||||
writeFile torrc $ unlines $
|
writeFile (fromOsPath torrc) $ unlines $
|
||||||
ls ++
|
ls ++
|
||||||
[ ""
|
[ ""
|
||||||
, "HiddenServiceDir " ++ hiddenServiceDir appname uid ident
|
, "HiddenServiceDir " ++ fromOsPath (hiddenServiceDir appname uid ident)
|
||||||
, "HiddenServicePort " ++ show newport ++
|
, "HiddenServicePort " ++ show newport ++
|
||||||
" unix:" ++ sockfile
|
" unix:" ++ fromOsPath sockfile
|
||||||
]
|
]
|
||||||
-- Reload tor, so it will see the new hidden
|
-- Reload tor, so it will see the new hidden
|
||||||
-- service and generate the hostname file for it.
|
-- service and generate the hostname file for it.
|
||||||
|
@ -109,7 +110,8 @@ addHiddenService appname uid ident = do
|
||||||
waithiddenservice :: Int -> OnionPort -> IO (OnionAddress, OnionPort)
|
waithiddenservice :: Int -> OnionPort -> IO (OnionAddress, OnionPort)
|
||||||
waithiddenservice 0 _ = giveup "tor failed to create hidden service, perhaps the tor service is not running"
|
waithiddenservice 0 _ = giveup "tor failed to create hidden service, perhaps the tor service is not running"
|
||||||
waithiddenservice n p = do
|
waithiddenservice n p = do
|
||||||
v <- tryIO $ readFile $ hiddenServiceHostnameFile appname uid ident
|
v <- tryIO $ readFile $ fromOsPath $
|
||||||
|
hiddenServiceHostnameFile appname uid ident
|
||||||
case v of
|
case v of
|
||||||
Right s | ".onion\n" `isSuffixOf` s ->
|
Right s | ".onion\n" `isSuffixOf` s ->
|
||||||
return (OnionAddress (takeWhile (/= '\n') s), p)
|
return (OnionAddress (takeWhile (/= '\n') s), p)
|
||||||
|
@ -122,11 +124,13 @@ addHiddenService appname uid ident = do
|
||||||
-- Has to be inside the torLibDir so tor can create it.
|
-- Has to be inside the torLibDir so tor can create it.
|
||||||
--
|
--
|
||||||
-- Has to end with "uid_ident" so getHiddenServiceSocketFile can find it.
|
-- Has to end with "uid_ident" so getHiddenServiceSocketFile can find it.
|
||||||
hiddenServiceDir :: AppName -> UserID -> UniqueIdent -> FilePath
|
hiddenServiceDir :: AppName -> UserID -> UniqueIdent -> OsPath
|
||||||
hiddenServiceDir appname uid ident = torLibDir </> appname ++ "_" ++ show uid ++ "_" ++ ident
|
hiddenServiceDir appname uid ident =
|
||||||
|
torLibDir </> toOsPath (appname ++ "_" ++ show uid ++ "_" ++ ident)
|
||||||
|
|
||||||
hiddenServiceHostnameFile :: AppName -> UserID -> UniqueIdent -> FilePath
|
hiddenServiceHostnameFile :: AppName -> UserID -> UniqueIdent -> OsPath
|
||||||
hiddenServiceHostnameFile appname uid ident = hiddenServiceDir appname uid ident </> "hostname"
|
hiddenServiceHostnameFile appname uid ident =
|
||||||
|
hiddenServiceDir appname uid ident </> literalOsPath "hostname"
|
||||||
|
|
||||||
-- | Location of the socket for a hidden service.
|
-- | Location of the socket for a hidden service.
|
||||||
--
|
--
|
||||||
|
@ -136,33 +140,36 @@ hiddenServiceHostnameFile appname uid ident = hiddenServiceDir appname uid ident
|
||||||
-- Note that some unix systems limit socket paths to 92 bytes long.
|
-- Note that some unix systems limit socket paths to 92 bytes long.
|
||||||
-- That should not be a problem if the UniqueIdent is around the length of
|
-- That should not be a problem if the UniqueIdent is around the length of
|
||||||
-- a UUID, and the AppName is short.
|
-- a UUID, and the AppName is short.
|
||||||
hiddenServiceSocketFile :: AppName -> UserID -> UniqueIdent -> FilePath
|
hiddenServiceSocketFile :: AppName -> UserID -> UniqueIdent -> OsPath
|
||||||
hiddenServiceSocketFile appname uid ident = varLibDir </> appname </> show uid ++ "_" ++ ident </> "s"
|
hiddenServiceSocketFile appname uid ident =
|
||||||
|
varLibDir </> toOsPath appname
|
||||||
|
</> toOsPath (show uid ++ "_" ++ ident) </> toOsPath "s"
|
||||||
|
|
||||||
-- | Parse torrc, to get the socket file used for a hidden service with
|
-- | Parse torrc, to get the socket file used for a hidden service with
|
||||||
-- the specified UniqueIdent.
|
-- the specified UniqueIdent.
|
||||||
getHiddenServiceSocketFile :: AppName -> UserID -> UniqueIdent -> IO (Maybe FilePath)
|
getHiddenServiceSocketFile :: AppName -> UserID -> UniqueIdent -> IO (Maybe OsPath)
|
||||||
getHiddenServiceSocketFile _appname uid ident =
|
getHiddenServiceSocketFile _appname uid ident =
|
||||||
parse . map words . lines <$> catchDefaultIO "" (readFile =<< findTorrc)
|
parse . map words . lines <$> catchDefaultIO ""
|
||||||
|
(readFile . fromOsPath =<< findTorrc)
|
||||||
where
|
where
|
||||||
parse [] = Nothing
|
parse [] = Nothing
|
||||||
parse (("HiddenServiceDir":hsdir:[]):("HiddenServicePort":_hsport:hsaddr:[]):rest)
|
parse (("HiddenServiceDir":hsdir:[]):("HiddenServicePort":_hsport:hsaddr:[]):rest)
|
||||||
| "unix:" `isPrefixOf` hsaddr && hasident hsdir =
|
| "unix:" `isPrefixOf` hsaddr && hasident (toOsPath hsdir) =
|
||||||
Just (drop (length "unix:") hsaddr)
|
Just $ toOsPath $ drop (length "unix:") hsaddr
|
||||||
| otherwise = parse rest
|
| otherwise = parse rest
|
||||||
parse (_:rest) = parse rest
|
parse (_:rest) = parse rest
|
||||||
|
|
||||||
-- Don't look for AppName in the hsdir, because it didn't used to
|
-- Don't look for AppName in the hsdir, because it didn't used to
|
||||||
-- be included.
|
-- be included.
|
||||||
hasident hsdir = (show uid ++ "_" ++ ident) `isSuffixOf` takeFileName hsdir
|
hasident hsdir = toOsPath (show uid ++ "_" ++ ident) `OS.isSuffixOf` takeFileName hsdir
|
||||||
|
|
||||||
-- | Sets up the directory for the socketFile, with appropriate
|
-- | Sets up the directory for the socketFile, with appropriate
|
||||||
-- permissions. Must run as root.
|
-- permissions. Must run as root.
|
||||||
prepHiddenServiceSocketDir :: AppName -> UserID -> UniqueIdent -> IO ()
|
prepHiddenServiceSocketDir :: AppName -> UserID -> UniqueIdent -> IO ()
|
||||||
prepHiddenServiceSocketDir appname uid ident = do
|
prepHiddenServiceSocketDir appname uid ident = do
|
||||||
createDirectoryIfMissing True d
|
createDirectoryIfMissing True d
|
||||||
setOwnerAndGroup (toRawFilePath d) uid (-1)
|
setOwnerAndGroup (fromOsPath d) uid (-1)
|
||||||
modifyFileMode (toRawFilePath d) $
|
modifyFileMode (fromOsPath d) $
|
||||||
addModes [ownerReadMode, ownerExecuteMode, ownerWriteMode]
|
addModes [ownerReadMode, ownerExecuteMode, ownerWriteMode]
|
||||||
where
|
where
|
||||||
d = takeDirectory $ hiddenServiceSocketFile appname uid ident
|
d = takeDirectory $ hiddenServiceSocketFile appname uid ident
|
||||||
|
@ -170,21 +177,23 @@ prepHiddenServiceSocketDir appname uid ident = do
|
||||||
-- | Finds the system's torrc file, in any of the typical locations of it.
|
-- | Finds the system's torrc file, in any of the typical locations of it.
|
||||||
-- Returns the first found. If there is no system torrc file, defaults to
|
-- Returns the first found. If there is no system torrc file, defaults to
|
||||||
-- /etc/tor/torrc.
|
-- /etc/tor/torrc.
|
||||||
findTorrc :: IO FilePath
|
findTorrc :: IO OsPath
|
||||||
findTorrc = fromMaybe "/etc/tor/torrc" <$> firstM doesFileExist
|
findTorrc = fromMaybe deftorrc <$> firstM doesFileExist
|
||||||
-- Debian
|
[ deftorrc
|
||||||
[ "/etc/tor/torrc"
|
|
||||||
-- Some systems put it here instead.
|
-- Some systems put it here instead.
|
||||||
, "/etc/torrc"
|
, literalOsPath "/etc/torrc"
|
||||||
-- Default when installed from source
|
-- Default when installed from source
|
||||||
, "/usr/local/etc/tor/torrc"
|
, literalOsPath "/usr/local/etc/tor/torrc"
|
||||||
]
|
]
|
||||||
|
where
|
||||||
|
-- Debian uses this
|
||||||
|
deftorrc = literalOsPath "/etc/tor/torrc"
|
||||||
|
|
||||||
torLibDir :: FilePath
|
torLibDir :: OsPath
|
||||||
torLibDir = "/var/lib/tor"
|
torLibDir = literalOsPath "/var/lib/tor"
|
||||||
|
|
||||||
varLibDir :: FilePath
|
varLibDir :: OsPath
|
||||||
varLibDir = "/var/lib"
|
varLibDir = literalOsPath "/var/lib"
|
||||||
|
|
||||||
torIsInstalled :: IO Bool
|
torIsInstalled :: IO Bool
|
||||||
torIsInstalled = inSearchPath "tor"
|
torIsInstalled = inSearchPath "tor"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue