Windows: Support long filenames in more (possibly all) of the code
authorJoey Hess <joeyh@joeyh.name>
Wed, 1 Mar 2023 19:55:58 +0000 (15:55 -0400)
committerJoey Hess <joeyh@joeyh.name>
Wed, 1 Mar 2023 19:55:58 +0000 (15:55 -0400)
Works around this bug in unix-compat:
https://github.com/jacobstanley/unix-compat/issues/56
getFileStatus and other FilePath using functions in unix-compat do not do
UNC conversion on Windows.

Made Utility.RawFilePath use convertToWindowsNativeNamespace to do the
necessary conversion on windows to support long filenames.

Audited all imports of System.PosixCompat.Files to make sure that no
functions that operate on FilePath were imported from it. Instead, use
the equvilants from Utility.RawFilePath. In particular the
re-export of that module in Common had to be removed, which led to lots
of other changes throughout the code.

The changes to Build.Configure, Build.DesktopFile, and Build.TestConfig
make Utility.Directory not be needed to build setup. And so let it use
Utility.RawFilePath, which depends on unix, which cannot be in
setup-depends.

Sponsored-by: Dartmouth College's Datalad project
57 files changed:
Annex/AutoMerge.hs
Annex/Content.hs
Annex/Content/LowLevel.hs
Annex/Content/PointerFile.hs
Annex/CopyFile.hs
Annex/Ingest.hs
Annex/Init.hs
Annex/Link.hs
Annex/Perms.hs
Annex/Tmp.hs
Assistant/Install.hs
Assistant/Threads/Committer.hs
Assistant/Threads/SanityChecker.hs
Assistant/Threads/Watcher.hs
Assistant/Upgrade.hs
Assistant/WebApp/Configurators/Local.hs
Backend/WORM.hs
Build/Configure.hs
Build/DesktopFile.hs
Build/TestConfig.hs
CHANGELOG
CmdLine/Seek.hs
Command/Add.hs
Command/Fix.hs
Command/Fsck.hs
Command/FuzzTest.hs
Command/Import.hs
Command/ImportFeed.hs
Command/Info.hs
Command/Lock.hs
Command/ReKey.hs
Command/Unannex.hs
Command/Uninit.hs
Command/Unlock.hs
Common.hs
Git/Hook.hs
Limit.hs
P2P/Address.hs
P2P/IO.hs
Remote/Ddar.hs
Remote/Directory.hs
Remote/Git.hs
Remote/Helper/Git.hs
Test.hs
Test/Framework.hs
Upgrade/V0.hs
Upgrade/V1.hs
Upgrade/V7.hs
Utility/DirWatcher/INotify.hs
Utility/DirWatcher/Win32Notify.hs
Utility/Directory.hs
Utility/InodeCache.hs
Utility/QuickCheck.hs
Utility/RawFilePath.hs
Utility/SshConfig.hs
Utility/Tmp.hs
Utility/Tor.hs

index f4f95ff44159a7fa128e6c941d9097135e0b401b..77afe521c9e36b42e6cf9802e491dbf800e45f1d 100644 (file)
@@ -39,6 +39,7 @@ import qualified Utility.RawFilePath as R
 import qualified Data.Set as S
 import qualified Data.Map as M
 import qualified Data.ByteString.Lazy as L
+import System.PosixCompat.Files (isSymbolicLink)
 
 {- Merges from a branch into the current branch (which may not exist yet),
  - with automatic merge conflict resolution.
index 589df4321509189c2ec33ecda02e34666831bdaa..00907030475c4ea678f2f95b5c8944321bda9398 100644 (file)
@@ -104,6 +104,7 @@ import Utility.Metered
 import qualified Utility.RawFilePath as R
 
 import qualified System.FilePath.ByteString as P
+import System.PosixCompat.Files (isSymbolicLink, linkCount)
 
 {- Prevents the content from being removed while the action is running.
  - Uses a shared lock.
index 0271fa65de4d0afd3034f32d6ffe7f87f6136127..947acabb1be136d4f0f058a533a569b5c0774fc4 100644 (file)
@@ -19,6 +19,7 @@ import Utility.CopyFile
 import qualified Utility.RawFilePath as R
 
 import qualified System.FilePath.ByteString as P
+import System.PosixCompat.Files (linkCount)
 
 {- Runs the secure erase command if set, otherwise does nothing.
  - File may or may not be deleted at the end; caller is responsible for
index 6c1ab8fd3b22f29f48ded215cf35cac98e7d9886..7fc4be53272fb78b467814e60a2c77de34486555 100644 (file)
@@ -22,6 +22,8 @@ import Utility.Touch
 import qualified System.Posix.Files as Posix
 #endif
 
+import System.PosixCompat.Files (fileMode)
+
 {- Populates a pointer file with the content of a key. 
  -
  - If the file already has some other content, it is not modified.
@@ -53,12 +55,11 @@ populatePointerFile restage k obj f = go =<< liftIO (isPointerFile f)
  - Does not check if the pointer file is modified. -}
 depopulatePointerFile :: Key -> RawFilePath -> Annex ()
 depopulatePointerFile key file = do
-       let file' = fromRawFilePath file
-       st <- liftIO $ catchMaybeIO $ getFileStatus file'
+       st <- liftIO $ catchMaybeIO $ R.getFileStatus file
        let mode = fmap fileMode st
        secureErase file
        liftIO $ removeWhenExistsWith R.removeLink file
-       ic <- replaceWorkTreeFile file' $ \tmp -> do
+       ic <- replaceWorkTreeFile (fromRawFilePath file) $ \tmp -> do
                let tmp' = toRawFilePath tmp
                liftIO $ writePointerFile tmp' key mode
 #if ! defined(mingw32_HOST_OS)
index 93ab346c7b99f678fe2b52c2583766953b39d579..8fa84bddcb2e81ec2146b6e23397edc99db6ebac 100644 (file)
@@ -15,10 +15,12 @@ import Utility.CopyFile
 import Utility.FileMode
 import Utility.Touch
 import Utility.Hash (IncrementalVerifier(..))
+import qualified Utility.RawFilePath as R
 
 import Control.Concurrent
 import qualified Data.ByteString as S
 import Data.Time.Clock.POSIX
+import System.PosixCompat.Files (fileMode)
 
 -- To avoid the overhead of trying copy-on-write every time, it's tried
 -- once and if it fails, is not tried again.
@@ -101,9 +103,9 @@ fileCopier copycowtried src dest meterupdate iv =
                        fileContentCopier hsrc dest meterupdate iv
                
                -- Copy src mode and mtime.
-               mode <- fileMode <$> getFileStatus src
+               mode <- fileMode <$> R.getFileStatus (toRawFilePath src)
                mtime <- utcTimeToPOSIXSeconds <$> getModificationTime src
-               setFileMode dest mode
+               R.setFileMode dest' mode
                touch dest' mtime False
 
                return Copied
index 89dc8aceaa216da433da93cfcc96255f0d765800..1dcbb2f6a63438252bef9da9581e0d242428e96c 100644 (file)
@@ -51,6 +51,8 @@ import Annex.AdjustedBranch
 import Annex.FileMatcher
 import qualified Utility.RawFilePath as R
 
+import System.PosixCompat.Files (fileMode)
+
 data LockedDown = LockedDown
        { lockDownConfig :: LockDownConfig
        , keySource :: KeySource
@@ -120,11 +122,12 @@ lockDown' cfg file = tryNonAsync $ ifM crippledFileSystem
                                `catchIO` const (nohardlink' delta)
 
        withhardlink' delta tmpfile = do
-               createLink file tmpfile
-               cache <- genInodeCache (toRawFilePath tmpfile) delta
+               let tmpfile' = toRawFilePath tmpfile
+               R.createLink file' tmpfile'
+               cache <- genInodeCache tmpfile' delta
                return $ LockedDown cfg $ KeySource
                        { keyFilename = file'
-                       , contentLocation = toRawFilePath tmpfile
+                       , contentLocation = tmpfile'
                        , inodeCache = cache
                        }
                
index 8b86572bec4fd814acb51ee2e9fda446845c2d40..b152f46aa589f12de9c11266130893bad30ba12f 100644 (file)
@@ -62,6 +62,7 @@ import qualified Utility.LockFile.Posix as Posix
 
 import qualified Data.Map as M
 import Control.Monad.IO.Class (MonadIO)
+import System.PosixCompat.Files (ownerReadMode, isNamedPipe)
 #ifndef mingw32_HOST_OS
 import Data.Either
 import qualified System.FilePath.ByteString as P
@@ -296,7 +297,7 @@ probeCrippledFileSystem' tmp freezecontent thawcontent hasfreezehook = do
        probe f = catchDefaultIO (True, []) $ do
                let f2 = f ++ "2"
                liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f2)
-               liftIO $ createSymbolicLink f f2
+               liftIO $ R.createSymbolicLink (toRawFilePath f) (toRawFilePath f2)
                liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f2)
                (fromMaybe (liftIO . preventWrite) freezecontent) (toRawFilePath f)
                -- Should be unable to write to the file (unless
@@ -372,7 +373,7 @@ probeFifoSupport = do
                        removeWhenExistsWith R.removeLink f
                        removeWhenExistsWith R.removeLink f2
                        ms <- tryIO $ do
-                               createNamedPipe (fromRawFilePath f) ownerReadMode
+                               R.createNamedPipe f ownerReadMode
                                R.createLink f f2
                                R.getFileStatus f
                        removeWhenExistsWith R.removeLink f
index ad2ce15478e20662ca22faff72fdf3cfb40589c2..9c75a8b72803ababfeeabb8c6258d427be2ee7af 100644 (file)
@@ -43,6 +43,7 @@ import qualified Data.ByteString as S
 import qualified Data.ByteString.Char8 as S8
 import qualified Data.ByteString.Lazy as L
 import qualified System.FilePath.ByteString as P
+import System.PosixCompat.Files (isSymbolicLink)
 
 type LinkTarget = S.ByteString
 
index e6ed48a4d05454bd18a90e445a13e905a6b05241..ce8c835fcca088bf02c0be4c60c6f49a66d6a254 100644 (file)
@@ -44,6 +44,8 @@ import Config
 import Utility.Directory.Create
 import qualified Utility.RawFilePath as R
 
+import System.PosixCompat.Files (fileMode, intersectFileModes, nullFileMode, groupWriteMode, ownerWriteMode, ownerReadMode, groupReadMode, stdFileMode, ownerExecuteMode, groupExecuteMode)
+
 withShared :: (SharedRepository -> Annex a) -> Annex a
 withShared a = a =<< coreSharedRepository <$> Annex.getGitConfig
 
index f602f568058b0c5d7fc1bc0e10cfcd3d90e6bc27..5293deea299b193a9566d8dd2f430115a13f24e9 100644 (file)
@@ -15,6 +15,7 @@ import Types.CleanupActions
 import qualified Utility.RawFilePath as R
 
 import Data.Time.Clock.POSIX
+import System.PosixCompat.Files (modificationTime)
 
 -- | For creation of tmp files, other than for key's contents.
 --
@@ -66,7 +67,7 @@ cleanupOtherTmp = do
        cleanold f = do
                now <- liftIO getPOSIXTime
                let oldenough = now - (60 * 60 * 24 * 7)
-               catchMaybeIO (modificationTime <$> getSymbolicLinkStatus f) >>= \case
+               catchMaybeIO (modificationTime <$> R.getSymbolicLinkStatus (toRawFilePath f)) >>= \case
                        Just mtime | realToFrac mtime <= oldenough -> 
                                void $ tryIO $ removeWhenExistsWith R.removeLink (toRawFilePath f)
                        _ -> return ()
index 6a31968d7b2de04eb405b168215205c64e05d733..c11b6d558547856bd6c60504152db023a98cbd14 100644 (file)
@@ -27,6 +27,8 @@ import Utility.UserInfo
 import Utility.Android
 #endif
 
+import System.PosixCompat.Files (ownerExecuteMode)
+
 standaloneAppBase :: IO (Maybe FilePath)
 standaloneAppBase = getEnv "GIT_ANNEX_APP_BASE"
 
index 7b06e7d76231acafb03a08c73ccce2efdef0ff63..2e9a25c1114781fb420ebe82c52507fde14f9ff6 100644 (file)
@@ -40,12 +40,14 @@ import qualified Database.Keys
 import qualified Command.Sync
 import Utility.Tuple
 import Utility.Metered
+import qualified Utility.RawFilePath as R
 
 import Data.Time.Clock
 import qualified Data.Set as S
 import qualified Data.Map as M
 import Data.Either
 import Control.Concurrent
+import System.PosixCompat.Files (fileID, deviceID, fileMode)
 
 {- This thread makes git commits at appropriate times. -}
 commitThread :: NamedThread
@@ -358,7 +360,7 @@ handleAdds lockdowndir havelsof largefilematcher delayadd cs = returnWhen (null
 
        done change file key = liftAnnex $ do
                logStatus key InfoPresent
-               mode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus file
+               mode <- liftIO $ catchMaybeIO $ fileMode <$> R.getFileStatus (toRawFilePath file)
                stagePointerFile (toRawFilePath file) mode =<< hashPointerFile key
                showEndOk
                return $ Just $ finishedChange change key
@@ -367,8 +369,8 @@ handleAdds lockdowndir havelsof largefilematcher delayadd cs = returnWhen (null
         - and is still a hard link to its contentLocation,
         - before ingesting it. -}
        sanitycheck keysource a = do
-               fs <- liftIO $ getSymbolicLinkStatus $ fromRawFilePath $ keyFilename keysource
-               ks <- liftIO $ getSymbolicLinkStatus $ fromRawFilePath $ contentLocation keysource
+               fs <- liftIO $ R.getSymbolicLinkStatus $ keyFilename keysource
+               ks <- liftIO $ R.getSymbolicLinkStatus $ contentLocation keysource
                if deviceID ks == deviceID fs && fileID ks == fileID fs
                        then a
                        else do
index 34f46c800313d3b817aebd45fbbf45829aa4acaa..87587e227fbf42acdaf23496638ea71ffa06d938 100644 (file)
@@ -53,6 +53,7 @@ import Utility.DiskFree
 
 import Data.Time.Clock.POSIX
 import qualified Data.Text as T
+import System.PosixCompat.Files (statusChangeTime, isSymbolicLink)
 
 {- This thread runs once at startup, and most other threads wait for it
  - to finish. (However, the webapp thread does not, to prevent the UI
@@ -156,11 +157,10 @@ dailyCheck urlrenderer = do
        (unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo [] False ["."] g
        now <- liftIO getPOSIXTime
        forM_ unstaged $ \file -> do
-               let file' = fromRawFilePath file
-               ms <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus file'
+               ms <- liftIO $ catchMaybeIO $ R.getSymbolicLinkStatus file
                case ms of
                        Just s  | toonew (statusChangeTime s) now -> noop
-                               | isSymbolicLink s -> addsymlink file' ms
+                               | isSymbolicLink s -> addsymlink (fromRawFilePath file) ms
                        _ -> noop
        liftIO $ void cleanup
 
index bbc4fa31c506285a16bf013945c8567abdfd8657..540ac5c6c5ac11dbf451b3fcfd7701ef82f813d2 100644 (file)
@@ -51,6 +51,7 @@ import Data.Typeable
 import qualified Data.ByteString.Lazy as L
 import qualified Control.Exception as E
 import Data.Time.Clock
+import System.PosixCompat.Files (fileMode, statusChangeTime)
 
 checkCanWatch :: Annex ()
 checkCanWatch
@@ -218,7 +219,7 @@ onAddFile symlinkssupported f fs = do
                unlessM (inAnnex oldkey) $
                        logStatus oldkey InfoMissing
        addlink file key = do
-               mode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus file
+               mode <- liftIO $ catchMaybeIO $ fileMode <$> R.getFileStatus (toRawFilePath file)
                liftAnnex $ stagePointerFile (toRawFilePath file) mode =<< hashPointerFile key
                madeChange file $ LinkChange (Just key)
 
index 7ef410e4525e289e825df5f58bfe68d13bc70b09..5b8a49ebf615a91edb2d2519a336350d38b53ac2 100644 (file)
@@ -222,7 +222,7 @@ upgradeToDistribution newdir cleanup distributionfile = do
        makeorigsymlink olddir = do
                let origdir = fromRawFilePath (parentDir (toRawFilePath olddir)) </> installBase
                removeWhenExistsWith R.removeLink (toRawFilePath origdir)
-               createSymbolicLink newdir origdir
+               R.createSymbolicLink (toRawFilePath newdir) (toRawFilePath origdir)
 
 {- Finds where the old version was installed. -}
 oldVersionLocation :: IO FilePath
index cbbe2eac9011e7a18eeb11a12bb96be2f1788dc1..a5d400bfb64969772c24c6e71c28d895fb069253 100644 (file)
@@ -40,6 +40,7 @@ import qualified Remote.GCrypt as GCrypt
 import qualified Types.Remote
 import Utility.Android
 import Types.ProposedAccepted
+import qualified Utility.RawFilePath as R
 
 import qualified Data.Text as T
 import qualified Data.Map as M
@@ -421,7 +422,7 @@ canWrite dir = do
                ( return dir
                , return $ fromRawFilePath $ parentDir $ toRawFilePath dir
                )
-       catchBoolIO $ fileAccess tocheck False True False
+       catchBoolIO $ R.fileAccess (toRawFilePath tocheck) False True False
 
 {- Gets the UUID of the git repo at a location, which may not exist, or
  - not be a git-annex repo. -}
index 233ca92e68e327048e15a18a02a1a98c2fc505dc..771a490e48376ffe8ca97e1b3e3e8b75aa681cc8 100644 (file)
@@ -14,10 +14,11 @@ import Types.KeySource
 import Backend.Utilities
 import Git.FilePath
 import Utility.Metered
+import qualified Utility.RawFilePath as R
 
 import qualified Data.ByteString.Char8 as S8
-import qualified Utility.RawFilePath as R
 import qualified Data.ByteString.Short as S (toShort, fromShort)
+import System.PosixCompat.Files (modificationTime)
 
 backends :: [Backend]
 backends = [backend]
index 02e33ebf2a4efacddfb31cb74bde4d165c182ca6..cce9488bae4090f41805bc77f500d718ceb6195d 100644 (file)
@@ -10,7 +10,7 @@ import Build.Version
 import Utility.SafeCommand
 import Utility.Env.Basic
 import qualified Git.Version
-import Utility.Directory
+import Utility.SystemDirectory
 
 import Control.Monad
 import Control.Applicative
index a4c4944adeddc07b4f6fc1d6eeb71a007fa09245..5c1b72c02163b9c5f57298709addde54d3a72cd2 100644 (file)
@@ -15,7 +15,7 @@ import Utility.Exception
 import Utility.FreeDesktop
 import Utility.Path
 import Utility.Monad
-import Utility.Directory
+import Utility.SystemDirectory
 import Utility.FileSystemEncoding
 import Config.Files
 import Utility.OSX
index 988db58a9a8ed03745550266dedbe7fe3d0c1063..5458612d4c2914d7ce3ec4d0ea86435f1628fb69 100644 (file)
@@ -7,7 +7,7 @@ module Build.TestConfig where
 import Utility.Path
 import Utility.Monad
 import Utility.SafeCommand
-import Utility.Directory
+import Utility.SystemDirectory
 
 import System.IO
 import System.FilePath
index 443ab2c962c863c140f991c420b6795e889f5991..2f0c8fc3cf84bc21db85c5292126f46f6621969d 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,7 @@ git-annex (10.20230228) UNRELEASED; urgency=medium
     view branch, will enter an adjusted view branch.
   * status: This command is deprecated because it was only needed in direct
     mode; git status --short is very similar.
+  * Windows: Support long filenames in more (possibly all) of the code.
 
  -- Joey Hess <id@joeyh.name>  Mon, 27 Feb 2023 12:31:14 -0400
 
index 4b9776a01bdd6828b4cd810a421a7983543059a3..ab557d42abadfc50cde9732a26b064875108f134 100644 (file)
@@ -52,6 +52,7 @@ import Control.Concurrent.STM
 import System.Posix.Types
 import Data.IORef
 import Data.Time.Clock.POSIX
+import System.PosixCompat.Files (isDirectory, isSymbolicLink, deviceID, fileID)
 import qualified System.FilePath.ByteString as P
 
 data AnnexedFileSeeker = AnnexedFileSeeker
@@ -114,7 +115,7 @@ withPathContents a params = do
        -- fail if the path that the user provided is a broken symlink,
        -- the same as it fails if the path that the user provided does not
        -- exist.
-       get p = ifM (isDirectory <$> getFileStatus p)
+       get p = ifM (isDirectory <$> R.getFileStatus p')
                ( map (\f -> 
                        let f' = toRawFilePath f
                        in (f', P.makeRelative (P.takeDirectory (P.dropTrailingPathSeparator p')) f'))
@@ -562,8 +563,9 @@ workTreeItems' (AllowHidden allowhidden) ww ps = case ww of
                currbranch <- getCurrentBranch
                stopattop <- prepviasymlink
                ps' <- flip filterM ps $ \p -> do
-                       relf <- liftIO $ relPathCwdToFile $ toRawFilePath p
-                       ifM (not <$> (exists p <||> hidden currbranch relf))
+                       let p' = toRawFilePath p
+                       relf <- liftIO $ relPathCwdToFile p'
+                       ifM (not <$> (exists p' <||> hidden currbranch relf))
                                ( prob (p ++ " not found")
                                , ifM (viasymlink stopattop (upFrom relf))
                                        ( prob (p ++ " is beyond a symbolic link")
@@ -574,7 +576,7 @@ workTreeItems' (AllowHidden allowhidden) ww ps = case ww of
                        then return NoWorkTreeItems
                        else return (WorkTreeItems ps')
        
-       exists p = isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)
+       exists p = isJust <$> liftIO (catchMaybeIO $ R.getSymbolicLinkStatus p)
 
        prepviasymlink = do
                repotopst <- inRepo $ 
index 20ec77ae82f868c0c564e0ecc71fedba3e9afbcd..aaf0c91b737587c6232528d6b83715e82ba99c67 100644 (file)
@@ -31,7 +31,7 @@ import Annex.CheckIgnore
 import qualified Utility.RawFilePath as R
 import qualified System.FilePath.ByteString as P
 
-import System.PosixCompat.Files (fileSize)
+import System.PosixCompat.Files (fileSize, isSymbolicLink, isRegularFile, modificationTime, fileID, deviceID, fileMode, ownerExecuteMode, intersectFileModes)
 
 cmd :: Command
 cmd = notBareRepo $ 
index 39853c8942106bad9b1fc94c61ba245ebe7e784d..83f0515f0153d66a0cea25451d19ad8f707ffa22 100644 (file)
@@ -19,9 +19,10 @@ import Annex.Link
 import qualified Database.Keys
 import qualified Utility.RawFilePath as R
 
+import System.PosixCompat.Files (fileMode, linkCount)
 #if ! defined(mingw32_HOST_OS)
-import Utility.Touch
 import qualified System.Posix.Files as Posix
+import Utility.Touch
 #endif
 
 cmd :: Command
index 77929f14baa3b8becfafbe1cfba9234b140ca69a..e0b357fcbc54b793f45ac57e56d84088784478b1 100644 (file)
@@ -49,6 +49,7 @@ import qualified Data.Set as S
 import qualified Data.Map as M
 import Data.Either
 import qualified System.FilePath.ByteString as P
+import System.PosixCompat.Files (fileMode, isSymbolicLink, modificationTime)
 
 cmd :: Command
 cmd = withAnnexOptions [jobsOption, jsonOptions, annexedMatchingOptions] $
index f12f4611914be54e6426a1a1867a31f79acc4ded..4fadffce30f1062379fc8aa6b530c956784d521d 100644 (file)
@@ -274,4 +274,4 @@ newDir parent = go (100 :: Int)
                        )
 
 doesnotexist :: FilePath -> IO Bool
-doesnotexist f = isNothing <$> catchMaybeIO (getSymbolicLinkStatus f)
+doesnotexist f = isNothing <$> catchMaybeIO (R.getSymbolicLinkStatus (toRawFilePath f))
index b24bbcfd5ffeaf2100ce867c60128b1ddd6b7d75..634365c69fabd164438cb695d7613271f6c2c2c3 100644 (file)
@@ -37,6 +37,7 @@ import Utility.Metered
 import qualified Utility.RawFilePath as R
 
 import Control.Concurrent.STM
+import System.PosixCompat.Files (isDirectory, isSymbolicLink, isRegularFile)
 
 cmd :: Command
 cmd = notBareRepo $
index b6ee11be9ed8546ed414d0fe9b07774a92a38716..a2a6284eb324c0e06abb25e247946b7d9780924c 100644 (file)
@@ -50,6 +50,7 @@ import Command.AddUrl (addWorkTree, checkRaw)
 import Annex.UntrustedFilePath
 import qualified Annex.Branch
 import Logs
+import qualified Utility.RawFilePath as R
 
 cmd :: Command
 cmd = notBareRepo $ withAnnexOptions [backendOption] $
@@ -312,7 +313,7 @@ performDownload' started addunlockedmatcher opts cache todownload = case locatio
                                let (d, base) = splitFileName file
                                in d </> show n ++ "_" ++ base
                tryanother = makeunique url (n + 1) file
-               alreadyexists = liftIO $ isJust <$> catchMaybeIO (getSymbolicLinkStatus f)
+               alreadyexists = liftIO $ isJust <$> catchMaybeIO (R.getSymbolicLinkStatus (toRawFilePath f))
                checksameurl k = ifM (elem url <$> getUrls k)
                        ( return Nothing
                        , tryanother
index 042b9d656b537a377185ae143358a89ad68be37d..3d0cc838592031ea23cf8768992dbf723b56f649 100644 (file)
@@ -13,6 +13,7 @@ import "mtl" Control.Monad.State.Strict
 import qualified Data.Map.Strict as M
 import qualified Data.Vector as V
 import qualified System.FilePath.ByteString as P
+import System.PosixCompat.Files (isDirectory)
 import Data.Ord
 import qualified Data.Semigroup as Sem
 import Prelude
@@ -47,6 +48,7 @@ import qualified Limit
 import Messages.JSON (DualDisp(..), ObjectMap(..))
 import Annex.BloomFilter
 import qualified Command.Unused
+import qualified Utility.RawFilePath as R
 
 -- a named computation that produces a statistic
 type Stat = StatState (Maybe (String, StatState String))
@@ -163,7 +165,7 @@ autoenableInfo = showCustom "info" (SeekInput []) $ do
        return True
 
 itemInfo :: InfoOptions -> (SeekInput, String) -> Annex ()
-itemInfo o (si, p) = ifM (isdir p)
+itemInfo o (si, p) = ifM (isdir (toRawFilePath p))
        ( dirInfo o p si
        , Remote.byName' p >>= \case
                Right r -> remoteInfo o r si
@@ -177,7 +179,7 @@ itemInfo o (si, p) = ifM (isdir p)
                        (_us, msg) -> noInfo p si msg
        )
   where
-       isdir = liftIO . catchBoolIO . (isDirectory <$$> getFileStatus)
+       isdir = liftIO . catchBoolIO . (isDirectory <$$> R.getFileStatus)
 
 noInfo :: String -> SeekInput -> String -> Annex ()
 noInfo s si msg = do
index d3eb7d3ff67b0619acb7be1c8bd2a0b49d4bd365..352abb374503b5a30ab1bdee4393264de64268a0 100644 (file)
@@ -20,6 +20,8 @@ import Annex.Ingest
 import Logs.Location
 import Git.FilePath
 import qualified Utility.RawFilePath as R
+
+import System.PosixCompat.Files (linkCount)
        
 cmd :: Command
 cmd = withAnnexOptions [jsonOptions, annexedMatchingOptions] $
index 91d0804222379807602310ba7f59e1c1f59f120f..63a081c74323a55d0edde35c6640e96a8e9e6668 100644 (file)
@@ -20,6 +20,8 @@ import Annex.WorkTree
 import Utility.InodeCache
 import qualified Utility.RawFilePath as R
 
+import System.PosixCompat.Files (linkCount, fileMode)
+
 cmd :: Command
 cmd = command "rekey" SectionPlumbing
        "change keys used for files"
index 0d990da074fc196dfd52286432d6dc6a1b737122..d876f79b0631b15c60967fc80fb33041693be65b 100644 (file)
@@ -19,6 +19,8 @@ import Annex.InodeSentinal
 import Git.FilePath
 import qualified Utility.RawFilePath as R
 
+import System.PosixCompat.Files (linkCount)
+
 cmd :: Command
 cmd = withAnnexOptions [annexedMatchingOptions] $
        command "unannex" SectionUtility
index d8cba0c4df5cd8213d25f3c0daa25f5fb03dd28e..57057e58f4a0b898b41c8decb693da56de374b88 100644 (file)
@@ -22,6 +22,8 @@ import Annex.WorkTree
 import Utility.FileMode
 import qualified Utility.RawFilePath as R
 
+import System.PosixCompat.Files (linkCount)
+
 cmd :: Command
 cmd = addCheck check $ 
        command "uninit" SectionUtility
index 7f76fdb03365fc2ac65a56d2cc1bce5155843099..b165025b4360b66af86ab851324204384633cb91 100644 (file)
@@ -18,6 +18,8 @@ import Git.FilePath
 import qualified Database.Keys
 import qualified Utility.RawFilePath as R
 
+import System.PosixCompat.Files (fileMode)
+
 cmd :: Command
 cmd = mkcmd "unlock" "unlock files for modification"
 
index 81be976210a15f1c11945db953e25e9f3a4ab38e..c430163063541313e0d1795c3d468ee4c58e77cc 100644 (file)
--- a/Common.hs
+++ b/Common.hs
@@ -13,7 +13,7 @@ import Data.Default as X
 import System.FilePath as X
 import System.IO as X hiding (FilePath)
 import System.Exit as X
-import System.PosixCompat.Files as X hiding (fileSize, removeLink, rename)
+import System.PosixCompat.Files as X (FileStatus)
 
 import Utility.Misc as X
 import Utility.Exception as X
index 788552305049af562456ccac0b9695bfe3c35921..39d653e02064bc48313aae0012c1c28055c5b327 100644 (file)
@@ -14,6 +14,9 @@ import Git
 import Utility.Tmp
 import Utility.Shell
 import Utility.FileMode
+import qualified Utility.RawFilePath as R
+
+import System.PosixCompat.Files (fileMode)
 
 data Hook = Hook
        { hookName :: FilePath
@@ -88,7 +91,7 @@ hookExists h r = do
        let f = hookFile h r
        catchBoolIO $
 #ifndef mingw32_HOST_OS
-               isExecutable . fileMode <$> getFileStatus f
+               isExecutable . fileMode <$> R.getFileStatus (toRawFilePath f)
 #else
                doesFileExist f
 #endif
index b105c9fb888c60b3d9b03e533f17bca451667505..9afaada438a914141b20860bc2c299ec0d35eec6 100644 (file)
--- a/Limit.hs
+++ b/Limit.hs
@@ -45,6 +45,7 @@ import Data.Time.Clock.POSIX
 import qualified Data.Set as S
 import qualified Data.Map as M
 import qualified System.FilePath.ByteString as P
+import System.PosixCompat.Files (accessTime, isSymbolicLink)
 
 {- Some limits can look at the current status of files on
  - disk, or in the annex. This allows controlling which happens. -}
@@ -272,7 +273,7 @@ matchLockStatus wantlocked (MatchingFile fi) = liftIO $ do
        islocked <- isPointerFile f >>= \case
                Just _key -> return False
                Nothing -> isSymbolicLink
-                       <$> getSymbolicLinkStatus (fromRawFilePath f)
+                       <$> R.getSymbolicLinkStatus f
        return (islocked == wantlocked)
 matchLockStatus wantlocked (MatchingInfo p) = 
        pure $ case providedLinkType p of
index 030e5ada63eecf9bd83a0a6890e2f05320883d14..a7b3c6db07cd92c05e1cf096ede5eeb578c276af 100644 (file)
@@ -14,8 +14,10 @@ import Git.Types
 import Creds
 import Utility.AuthToken
 import Utility.Tor
+import qualified Utility.RawFilePath as R
 
 import qualified Data.Text as T
+import System.PosixCompat.Files (fileOwner, fileGroup)
 
 -- | A P2P address, without an AuthToken.
 --
@@ -80,8 +82,9 @@ storeP2PAddress addr = do
                -- This may be run by root, so make the creds file
                -- and directory have the same owner and group as
                -- the git repository directory has.
-               st <- liftIO . getFileStatus =<< Annex.fromRepo repoLocation
-               let fixowner f = setOwnerAndGroup f (fileOwner st) (fileGroup st)
+               st <- liftIO . R.getFileStatus . toRawFilePath
+                       =<< Annex.fromRepo repoLocation
+               let fixowner f = R.setOwnerAndGroup (toRawFilePath f) (fileOwner st) (fileGroup st)
                liftIO $ do
                        fixowner tmpf
                        fixowner (takeDirectory tmpf)
index 875ed02f099fc48d7bf9b5b46d4262ded61fc43b..b37ce058c07b9b13cc4e67c94861d540a3526bc1 100644 (file)
--- a/P2P/IO.hs
+++ b/P2P/IO.hs
@@ -50,6 +50,7 @@ import Control.Concurrent.STM
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as L
 import qualified Network.Socket as S
+import System.PosixCompat.Files (groupReadMode, groupWriteMode, otherReadMode, otherWriteMode)
 
 -- Type of interpreters of the Proto free monad.
 type RunProto m = forall a. Proto a -> m (Either ProtoFailure a)
index bdbb2e222ea4180abd58a597b744b27fb1319e9b..37b6d03c1f5a8adedfb9393a4969e17bd1b45902 100644 (file)
@@ -14,6 +14,7 @@ module Remote.Ddar (remote) where
 import qualified Data.Map as M
 import qualified Data.ByteString.Lazy as L
 import System.IO.Error
+import System.PosixCompat.Files (isDirectory)
 
 import Annex.Common
 import Types.Remote
@@ -28,6 +29,7 @@ import Annex.Ssh
 import Annex.UUID
 import Utility.SshHost
 import Types.ProposedAccepted
+import qualified Utility.RawFilePath as R
 
 data DdarRepo = DdarRepo
        { ddarRepoConfig :: RemoteGitConfig
@@ -185,7 +187,7 @@ ddarDirectoryExists :: DdarRepo -> Annex (Either String Bool)
 ddarDirectoryExists ddarrepo
        | ddarLocal ddarrepo = do
                maybeStatus <- liftIO $ tryJust (guard . isDoesNotExistError) $
-                       getSymbolicLinkStatus $ ddarRepoLocation ddarrepo
+                       R.getSymbolicLinkStatus $ toRawFilePath $ ddarRepoLocation ddarrepo
                return $ case maybeStatus of
                        Left _ -> Right False
                        Right status -> Right $ isDirectory status
index dcefa0a9e72e2b0cec139f1047dff8dc2f4ad593..d495d42fdd2d00937c44a1e8ed9354ca67a34b68 100644 (file)
@@ -19,6 +19,7 @@ import qualified Data.ByteString.Lazy as L
 import qualified Data.Map as M
 import qualified System.FilePath.ByteString as P
 import Data.Default
+import System.PosixCompat.Files (isRegularFile, getFdStatus, deviceID)
 
 import Annex.Common
 import Types.Remote
@@ -254,7 +255,7 @@ retrieveKeyFileCheapM _ (LegacyChunks _) = Nothing
 retrieveKeyFileCheapM d NoChunks = Just $ \k _af f -> liftIO $ do
        file <- fromRawFilePath <$> (absPath =<< getLocation d k)
        ifM (doesFileExist file)
-               ( createSymbolicLink file f
+               ( R.createSymbolicLink (toRawFilePath file) (toRawFilePath f)
                , giveup "content file not present in remote"
                )
 #else
@@ -522,7 +523,7 @@ storeExportWithContentIdentifierM ii dir cow src _k loc overwritablecids p = do
                void $ liftIO $ fileCopier cow src tmpf p Nothing
                let tmpf' = toRawFilePath tmpf
                resetAnnexFilePerm tmpf'
-               liftIO (getSymbolicLinkStatus tmpf) >>= liftIO . mkContentIdentifier ii tmpf' >>= \case
+               liftIO (R.getSymbolicLinkStatus tmpf') >>= liftIO . mkContentIdentifier ii tmpf' >>= \case
                        Nothing -> giveup "unable to generate content identifier"
                        Just newcid -> do
                                checkExportContent ii dir loc
index d42b0fa39611c7fc41e593aaa1829ce2aa27d386..c65d1388d1f05fd3891040e7a567faa57f3a0b68 100644 (file)
@@ -700,7 +700,7 @@ type FileCopier = FilePath -> FilePath -> Key -> MeterUpdate -> Annex Bool -> Ve
 mkFileCopier :: Bool -> State -> Annex FileCopier
 mkFileCopier remotewanthardlink (State _ _ copycowtried _ _) = do
        localwanthardlink <- wantHardLink
-       let linker = \src dest -> createLink src dest >> return True
+       let linker = \src dest -> R.createLink (toRawFilePath src) (toRawFilePath dest) >> return True
        if remotewanthardlink || localwanthardlink
                then return $ \src dest k p check verifyconfig ->
                        ifM (liftIO (catchBoolIO (linker src dest)))
index 5fd7ea1e2ad81f775df6143805d9127d2b6aef2e..b9fb9008496fb343abd19cd1db48bc66f59cc931 100644 (file)
@@ -11,8 +11,10 @@ import Annex.Common
 import qualified Git
 import Types.Availability
 import qualified Types.Remote as Remote
+import qualified Utility.RawFilePath as R
 
 import Data.Time.Clock.POSIX
+import System.PosixCompat.Files (modificationTime)
 
 repoCheap :: Git.Repo -> Bool
 repoCheap = not . Git.repoIsUrl
@@ -37,7 +39,7 @@ guardUsable r fallback a
 gitRepoInfo :: Remote -> Annex [(String, String)]
 gitRepoInfo r = do
        d <- fromRawFilePath <$> fromRepo Git.localGitDir
-       mtimes <- liftIO $ mapM (modificationTime <$$> getFileStatus)
+       mtimes <- liftIO $ mapM (\p -> modificationTime <$> R.getFileStatus (toRawFilePath p))
                =<< dirContentsRecursive (d </> "refs" </> "remotes" </> Remote.name r)
        let lastsynctime = case mtimes of
                [] -> "never"
diff --git a/Test.hs b/Test.hs
index 961514236bbff27c75751d83b954a8488d96cf64..c16e597d71c37428d22627db70d5d7569e5842aa 100644 (file)
--- a/Test.hs
+++ b/Test.hs
@@ -26,9 +26,9 @@ import Control.Concurrent.STM hiding (check)
 
 import Common
 import CmdLine.GitAnnex.Options
+import qualified Utility.RawFilePath as R
 
 import qualified Utility.ShellEscape
-import qualified Utility.RawFilePath as R
 import qualified Annex
 import qualified Git.Filename
 import qualified Git.Types
@@ -1498,7 +1498,7 @@ test_nonannexed_symlink_conflict_resolution = do
                                        git_annex "sync" [] "sync in r1"
                                indir r2 $ do
                                        disconnectOrigin
-                                       createSymbolicLink symlinktarget "conflictor"
+                                       R.createSymbolicLink (toRawFilePath symlinktarget) (toRawFilePath "conflictor")
                                        git "add" [conflictor] "git add conflictor"
                                        git_annex "sync" [] "sync in r2"
                                pair r1 r2
@@ -1518,8 +1518,8 @@ test_nonannexed_symlink_conflict_resolution = do
                length v == 1
                        @? (what ++ " too many variant files in: " ++ show v)
                conflictor `elem` l @? (what ++ " conflictor file missing in: " ++ show l)
-               s <- catchMaybeIO (readSymbolicLink (d </> conflictor))
-               s == Just symlinktarget
+               s <- catchMaybeIO (R.readSymbolicLink (toRawFilePath (d </> conflictor)))
+               s == Just (toRawFilePath symlinktarget)
                        @? (what ++ " wrong target for nonannexed symlink: " ++ show s)
 
 {- Check merge conflict resolution when there is a local file,
index 28e951fe86b74af75427669abcab64e3b0e43052..8653dca1be41f62e8e5bcd69741f9c2c205c8327 100644 (file)
@@ -25,10 +25,12 @@ import System.Console.Concurrent
 import System.Console.ANSI
 import GHC.Conc
 import System.IO.Unsafe (unsafePerformIO)
+import System.PosixCompat.Files (isSymbolicLink, isRegularFile, fileMode, unionFileModes, ownerWriteMode)
 
 import Common
 import Types.Test
 import Types.Concurrency
+import qualified Utility.RawFilePath as R
 
 import qualified Annex
 import qualified Annex.UUID
@@ -351,24 +353,24 @@ checklink f = ifM (annexeval Config.crippledFileSystem)
        ( (isJust <$> annexeval (Annex.Link.getAnnexLinkTarget (toRawFilePath f)))
                @? f ++ " is not a (crippled) symlink"
        , do
-               s <- getSymbolicLinkStatus f
+               s <- R.getSymbolicLinkStatus (toRawFilePath f)
                isSymbolicLink s @? f ++ " is not a symlink"
        )
 
 checkregularfile :: FilePath -> Assertion
 checkregularfile f = do
-       s <- getSymbolicLinkStatus f
+       s <- R.getSymbolicLinkStatus (toRawFilePath f)
        isRegularFile s @? f ++ " is not a normal file"
        return ()
 
 checkdoesnotexist :: FilePath -> Assertion
 checkdoesnotexist f = 
-       (either (const True) (const False) <$> Utility.Exception.tryIO (getSymbolicLinkStatus f))
+       (either (const True) (const False) <$> Utility.Exception.tryIO (R.getSymbolicLinkStatus (toRawFilePath f)))
                @? f ++ " exists unexpectedly"
 
 checkexists :: FilePath -> Assertion
 checkexists f = 
-       (either (const False) (const True) <$> Utility.Exception.tryIO (getSymbolicLinkStatus f))
+       (either (const False) (const True) <$> Utility.Exception.tryIO (R.getSymbolicLinkStatus (toRawFilePath f)))
                @? f ++ " does not exist"
 
 checkcontent :: FilePath -> Assertion
@@ -381,14 +383,14 @@ checkunwritable f = do
        -- Look at permissions bits rather than trying to write or
        -- using fileAccess because if run as root, any file can be
        -- modified despite permissions.
-       s <- getFileStatus f
+       s <- R.getFileStatus (toRawFilePath f)
        let mode = fileMode s
        when (mode == mode `unionFileModes` ownerWriteMode) $
                assertFailure $ "able to modify annexed file's " ++ f ++ " content"
 
 checkwritable :: FilePath -> Assertion
 checkwritable f = do
-       s <- getFileStatus f
+       s <- R.getFileStatus (toRawFilePath f)
        let mode = fileMode s
        unless (mode == mode `unionFileModes` ownerWriteMode) $
                assertFailure $ "unable to modify " ++ f
index 3e6d9e6202cc5ea091cd74c4e6c00154397b02b1..27dfab4c7f26871777d687d415a06e9bb99a1bcf 100644 (file)
@@ -11,6 +11,9 @@ import Annex.Common
 import Types.Upgrade
 import Annex.Content
 import qualified Upgrade.V1
+import qualified Utility.RawFilePath as R
+
+import System.PosixCompat.Files (isRegularFile)
 
 upgrade :: Annex UpgradeResult
 upgrade = do
@@ -46,7 +49,8 @@ getKeysPresent0 dir = ifM (liftIO $ doesDirectoryExist dir)
   where
        present d = do
                result <- tryIO $
-                       getFileStatus $ dir ++ "/" ++ takeFileName d
+                       R.getFileStatus $ toRawFilePath $
+                               dir ++ "/" ++ takeFileName d
                case result of
                        Right s -> return $ isRegularFile s
                        Left _ -> return False
index 107db0361515c9478f77f089a5930dd979f3b383..4f6dfca226f27ebe7fe9e6f845d40824e3ed07b4 100644 (file)
@@ -15,6 +15,7 @@ import qualified Data.ByteString as S
 import qualified Data.ByteString.Short as S (toShort, fromShort)
 import qualified Data.ByteString.Lazy as L
 import qualified System.FilePath.ByteString as P
+import System.PosixCompat.Files (isRegularFile)
 
 import Annex.Common
 import Types.Upgrade
@@ -30,6 +31,7 @@ import Backend
 import Utility.FileMode
 import Utility.Tmp
 import qualified Upgrade.V2
+import qualified Utility.RawFilePath as R
 
 -- v2 adds hashing of filenames of content and location log files.
 -- Key information is encoded in filenames differently, so
@@ -101,7 +103,7 @@ updateSymlinks = do
                                link <- fromRawFilePath
                                        <$> calcRepo (gitAnnexLink (toRawFilePath f) k)
                                liftIO $ removeFile f
-                               liftIO $ createSymbolicLink link f
+                               liftIO $ R.createSymbolicLink (toRawFilePath link) (toRawFilePath f)
                                Annex.Queue.addCommand [] "add" [Param "--"] [f]
 
 moveLocationLogs :: Annex ()
@@ -203,7 +205,8 @@ lookupKey1 file = do
                Left _ -> return Nothing
                Right l -> makekey l
   where
-       getsymlink = takeFileName <$> readSymbolicLink file
+       getsymlink = takeFileName . fromRawFilePath
+               <$> R.readSymbolicLink (toRawFilePath file)
        makekey l = maybeLookupBackendVariety (fromKey keyVariety k) >>= \case
                Nothing -> do
                        unless (null kname || null bname ||
@@ -232,7 +235,7 @@ getKeyFilesPresent1' dir =
                )
   where
        present f = do
-               result <- tryIO $ getFileStatus f
+               result <- tryIO $ R.getFileStatus (toRawFilePath f)
                case result of
                        Right s -> return $ isRegularFile s
                        Left _ -> return False
index 219c11ed144c314e61ec4b8d78c9da157b6d9805..b3d1bd630fccc68b6f5d6092e2863ab0d9a95a67 100644 (file)
@@ -24,6 +24,7 @@ import Config
 import qualified Utility.RawFilePath as R
 
 import qualified System.FilePath.ByteString as P
+import System.PosixCompat.Files (isSymbolicLink)
 
 upgrade :: Bool -> Annex UpgradeResult
 upgrade automatic = do
@@ -110,7 +111,7 @@ populateKeysDb = unlessM isBareRepo $ do
        (l, cleanup) <- inRepo $ LsFiles.inodeCaches [top]
        forM_ l $ \case
                (_f, Nothing) -> giveup "Unable to parse git ls-files --debug output while upgrading git-annex sqlite databases."
-               (f, Just ic) -> unlessM (liftIO $ catchBoolIO $ isSymbolicLink <$> getSymbolicLinkStatus f) $ do
+               (f, Just ic) -> unlessM (liftIO $ catchBoolIO $ isSymbolicLink <$> R.getSymbolicLinkStatus (toRawFilePath f)) $ do
                        catKeyFile (toRawFilePath f) >>= \case
                                Nothing -> noop
                                Just k -> do
index c33b02fa6bae939e21d305fcc3eaca93e94bdd29..700bff577366a5196147bdb1224c4c7d92883e97 100644 (file)
@@ -7,9 +7,10 @@
 
 module Utility.DirWatcher.INotify (watchDir) where
 
-import Common hiding (isDirectory)
+import Common
 import Utility.ThreadLock
 import Utility.DirWatcher.Types
+import qualified Utility.RawFilePath as R
 
 import System.INotify
 import qualified System.Posix.Files as Files
@@ -149,14 +150,14 @@ watchDir i dir ignored scanevents hooks
 
        indir f = dir </> f
 
-       getstatus f = catchMaybeIO $ getSymbolicLinkStatus $ indir f
+       getstatus f = catchMaybeIO $ R.getSymbolicLinkStatus $ toRawFilePath $ indir f
        checkfiletype check h f = do
                ms <- getstatus f
                case ms of
                        Just s
                                | check s -> runhook h f ms
                        _ -> noop
-       filetype t f = catchBoolIO $ t <$> getSymbolicLinkStatus (indir f)
+       filetype t f = catchBoolIO $ t <$> R.getSymbolicLinkStatus (toRawFilePath (indir f))
 
        failedaddwatch e
                -- Inotify fails when there are too many watches with a
index 7a76f618a72781cd2d28c9187d00ce2124108272..7eba40de97fb33aea42a02db65bfccf11a361ef9 100644 (file)
@@ -11,7 +11,7 @@ import Common hiding (isDirectory)
 import Utility.DirWatcher.Types
 
 import System.Win32.Notify
-import qualified System.PosixCompat.Files as Files
+import qualified System.PosixCompat.Files (isRegularFile)
 
 watchDir :: FilePath -> (FilePath -> Bool) -> Bool -> WatchHooks -> IO WatchManager
 watchDir dir ignored scanevents hooks = do
@@ -51,7 +51,7 @@ watchDir dir ignored scanevents hooks = do
                                case ms of
                                        Nothing -> noop
                                        Just s
-                                               | Files.isRegularFile s ->
+                                               | isRegularFile s ->
                                                        when scanevents $
                                                                runhook addHook ms
                                                | otherwise ->
index b98d2213698374a400c1b15f3b54231798074964..a5c023f5cd065188603fa3370045f99478bb7679 100644 (file)
@@ -16,7 +16,7 @@ module Utility.Directory (
 
 import Control.Monad
 import System.FilePath
-import System.PosixCompat.Files (getSymbolicLinkStatus, isDirectory, isSymbolicLink)
+import System.PosixCompat.Files (isDirectory, isSymbolicLink)
 import Control.Applicative
 import System.IO.Unsafe (unsafeInterleaveIO)
 import Data.Maybe
@@ -25,7 +25,8 @@ import Prelude
 import Utility.SystemDirectory
 import Utility.Exception
 import Utility.Monad
-import Utility.Applicative
+import Utility.FileSystemEncoding
+import qualified Utility.RawFilePath as R
 
 dirCruft :: FilePath -> Bool
 dirCruft "." = True
@@ -65,7 +66,7 @@ dirContentsRecursiveSkipping skipdir followsubdirsymlinks topdir = go [topdir]
                | otherwise = do
                        let skip = collect (entry:files) dirs' entries
                        let recurse = collect files (entry:dirs') entries
-                       ms <- catchMaybeIO $ getSymbolicLinkStatus entry
+                       ms <- catchMaybeIO $ R.getSymbolicLinkStatus (toRawFilePath entry)
                        case ms of
                                (Just s) 
                                        | isDirectory s -> recurse
@@ -87,9 +88,10 @@ dirTreeRecursiveSkipping skipdir topdir = go [] [topdir]
                | skipdir (takeFileName dir) = go c dirs
                | otherwise = unsafeInterleaveIO $ do
                        subdirs <- go []
-                               =<< filterM (isDirectory <$$> getSymbolicLinkStatus)
+                               =<< filterM isdir
                                =<< catchDefaultIO [] (dirContents dir)
                        go (subdirs++dir:c) dirs
+       isdir p = isDirectory <$> R.getSymbolicLinkStatus (toRawFilePath p)
 
 {- Use with an action that removes something, which may or may not exist.
  -
index 27ee0b2cc38a71a15984349efc7853a955c104e6..a9a6bb1906340ebd5b4e2eb37e432cbbd2809a8f 100644 (file)
@@ -51,6 +51,7 @@ import Utility.QuickCheck
 import qualified Utility.RawFilePath as R
 
 import System.PosixCompat.Types
+import System.PosixCompat.Files (isRegularFile, fileID)
 import Data.Time.Clock.POSIX
 
 #ifdef mingw32_HOST_OS
index 650f559324f621544fd3640a2c52fa3d4e778471..96e31d5c08c1fb18905fa2427a631d1a1e4a3864 100644 (file)
@@ -6,6 +6,7 @@
  -}
 
 {-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# OPTIONS_GHC -fno-warn-tabs #-}
 {-# LANGUAGE TypeSynonymInstances #-}
 
 module Utility.QuickCheck
index 9d4f427e2e573c5d8c1dc1a098b49664f8ddbec3..b39423df5bbfd3d0600e3c838f40734f5ba484e4 100644 (file)
@@ -5,9 +5,11 @@
  -
  - On Windows, filenames are in unicode, so RawFilePaths have to be
  - decoded. So this library will work, but less efficiently than using
- - FilePath would.
+ - FilePath would. However, this library also takes care to support long
+ - filenames on Windows, by either using other libraries that do, or by
+ - doing UNC-style conversion itself.
  -
- - Copyright 2019-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2019-2023 Joey Hess <id@joeyh.name>
  -
  - License: BSD-2-clause
  -}
@@ -27,7 +29,10 @@ module Utility.RawFilePath (
        getCurrentDirectory,
        createDirectory,
        setFileMode,
+       setOwnerAndGroup,
        rename,
+       createNamedPipe,
+       fileAccess,
 ) where
 
 #ifndef mingw32_HOST_OS
@@ -48,23 +53,28 @@ createDirectory p = D.createDirectory p 0o777
 
 #else
 import System.PosixCompat (FileStatus, FileMode)
+-- System.PosixCompat does not handle UNC-style conversion itself,
+-- so all uses of it library have to be pre-converted below. See
+-- https://github.com/jacobstanley/unix-compat/issues/56
 import qualified System.PosixCompat as P
-import qualified System.PosixCompat.Files as F
 import qualified System.Directory as D
 import Utility.FileSystemEncoding
+import Utility.Path.Windows
 
 readSymbolicLink :: RawFilePath -> IO RawFilePath
 readSymbolicLink f = toRawFilePath <$> P.readSymbolicLink (fromRawFilePath f)
 
 createSymbolicLink :: RawFilePath -> RawFilePath -> IO ()
-createSymbolicLink a b = P.createSymbolicLink
-       (fromRawFilePath a)
-       (fromRawFilePath b)
+createSymbolicLink a b = do
+       a' <- fromRawFilePath <$> convertToWindowsNativeNamespace a
+       b' <- fromRawFilePath <$> convertToWindowsNativeNamespace b
+       P.createSymbolicLink a' b'
 
 createLink :: RawFilePath -> RawFilePath -> IO ()
-createLink a b = P.createLink
-       (fromRawFilePath a)
-       (fromRawFilePath b)
+createLink a b = do
+       a' <- fromRawFilePath <$> convertToWindowsNativeNamespace a
+       b' <- fromRawFilePath <$> convertToWindowsNativeNamespace b
+       P.createLink a' b'
 
 {- On windows, removeLink is not available, so only remove files,
  - not symbolic links. -}
@@ -72,10 +82,12 @@ removeLink :: RawFilePath -> IO ()
 removeLink = D.removeFile . fromRawFilePath
 
 getFileStatus :: RawFilePath -> IO FileStatus
-getFileStatus = P.getFileStatus . fromRawFilePath
+getFileStatus p = P.getFileStatus . fromRawFilePath
+       =<< convertToWindowsNativeNamespace p
 
 getSymbolicLinkStatus :: RawFilePath -> IO FileStatus
-getSymbolicLinkStatus = P.getSymbolicLinkStatus . fromRawFilePath
+getSymbolicLinkStatus p = P.getSymbolicLinkStatus . fromRawFilePath
+       =<< convertToWindowsNativeNamespace p
 
 doesPathExist :: RawFilePath -> IO Bool
 doesPathExist = D.doesPathExist . fromRawFilePath
@@ -87,10 +99,27 @@ createDirectory :: RawFilePath -> IO ()
 createDirectory = D.createDirectory . fromRawFilePath
 
 setFileMode :: RawFilePath -> FileMode -> IO () 
-setFileMode = F.setFileMode . fromRawFilePath
+setFileMode p m = do
+       p' <- fromRawFilePath <$> convertToWindowsNativeNamespace p
+       P.setFileMode p' m
 
 {- Using renamePath rather than the rename provided in unix-compat
  - because of this bug https://github.com/jacobstanley/unix-compat/issues/56-}
 rename :: RawFilePath -> RawFilePath -> IO ()
 rename a b = D.renamePath (fromRawFilePath a) (fromRawFilePath b)
+
+setOwnerAndGroup :: RawFilePath -> P.UserID -> P.GroupID -> IO ()
+setOwnerAndGroup p u g = do
+       p' <- fromRawFilePath <$> convertToWindowsNativeNamespace p
+       P.setOwnerAndGroup p' u g
+
+createNamedPipe :: RawFilePath -> FileMode -> IO ()
+createNamedPipe p m = do
+       p' <- fromRawFilePath <$> convertToWindowsNativeNamespace p
+       P.createNamedPipe p' m
+
+fileAccess :: RawFilePath -> Bool -> Bool -> Bool -> IO Bool
+fileAccess p a b c = do
+       p' <- fromRawFilePath <$> convertToWindowsNativeNamespace p
+       P.fileAccess p' a b c
 #endif
index bac38c530ffb64e608bded1e934a3abe8193f230..83c63fcd3d429bfb6d7a90884c2982f6ac1fb368 100644 (file)
@@ -32,6 +32,7 @@ import Utility.FileMode
 import Data.Char
 import Data.Ord
 import Data.Either
+import System.PosixCompat.Files (groupWriteMode, otherWriteMode)
 
 data SshConfig
        = GlobalConfig SshSetting
index 92bd921bc80e5120150dff8674f2f9f312effe26..efb15bd9b363f03f6222f0af1a21348eb3c1c3b8 100644 (file)
@@ -21,12 +21,12 @@ import System.IO
 import System.FilePath
 import System.Directory
 import Control.Monad.IO.Class
-import System.PosixCompat.Files hiding (removeLink)
 import System.IO.Error
 
 import Utility.Exception
 import Utility.FileSystemEncoding
 import Utility.FileMode
+import qualified Utility.RawFilePath as R
 
 type Template = String
 
@@ -62,14 +62,15 @@ viaTmp a file content = bracketIO setup cleanup use
                _ <- tryIO $ hClose h
                tryIO $ removeFile tmpfile
        use (tmpfile, h) = do
+               let tmpfile' = toRawFilePath tmpfile
                -- Make mode the same as if the file were created usually,
                -- not as a temp file. (This may fail on some filesystems
                -- that don't support file modes well, so ignore
                -- exceptions.)
-               _ <- liftIO $ tryIO $ setFileMode tmpfile =<< defaultFileMode
+               _ <- liftIO $ tryIO $ R.setFileMode tmpfile' =<< defaultFileMode
                liftIO $ hClose h
                a tmpfile content
-               liftIO $ rename tmpfile file
+               liftIO $ R.rename tmpfile' (toRawFilePath file)
 
 {- Runs an action with a tmp file located in the system's tmp directory
  - (or in "." if there is none) then removes the file. -}
index a9d1037a137429164c210a718f6655fdb1a7f173..9147e9b049d92854f434f59c116e6b9eb18cc222 100644 (file)
@@ -22,8 +22,10 @@ module Utility.Tor (
 import Common
 import Utility.ThreadScheduler
 import Utility.FileMode
+import Utility.RawFilePath (setOwnerAndGroup)
 
 import System.PosixCompat.Types
+import System.PosixCompat.Files (ownerReadMode, ownerWriteMode, ownerExecuteMode)
 import Data.Char
 import Network.Socket
 import Network.Socks5
@@ -165,7 +167,7 @@ getHiddenServiceSocketFile _appname uid ident =
 prepHiddenServiceSocketDir :: AppName -> UserID -> UniqueIdent -> IO ()
 prepHiddenServiceSocketDir appname uid ident = do
        createDirectoryIfMissing True d
-       setOwnerAndGroup d uid (-1)
+       setOwnerAndGroup (toRawFilePath d) uid (-1)
        modifyFileMode (toRawFilePath d) $
                addModes [ownerReadMode, ownerExecuteMode, ownerWriteMode]
   where