1+ {-# LANGUAGE CPP #-}
12{-# LANGUAGE TypeApplications #-}
23{-# LANGUAGE BangPatterns #-}
4+ {-# LANGUAGE ViewPatterns #-}
5+ {-# LANGUAGE QuasiQuotes #-}
36
47module System.File.OsPath.Internal where
58
69
710import qualified System.File.Platform as P
811
9- import Prelude ((.) , ($) , String , IO , ioError , pure , either , const , flip , Maybe (.. ), fmap , (<$>) , id , Bool (.. ), FilePath , (++) , return , show , (>>=) )
12+ import Prelude ((.) , ($) , String , IO , ioError , pure , either , const , flip , Maybe (.. ), fmap , (<$>) , id , Bool (.. ), FilePath , (++) , return , show , (>>=) , (==) , otherwise , userError )
1013import GHC.IO (catchException )
1114import GHC.IO.Exception (IOException (.. ))
1215import GHC.IO.Handle (hClose_help )
@@ -15,14 +18,20 @@ import GHC.IO.Handle.Types (Handle__, Handle(..))
1518import Control.Concurrent.MVar
1619import Control.Monad (void , when )
1720import Control.DeepSeq (force )
18- import Control.Exception (SomeException , try , evaluate , mask , onException )
21+ import Control.Exception (SomeException , try , evaluate , mask , onException , throwIO )
1922import System.IO (IOMode (.. ), hSetBinaryMode , hClose )
2023import System.IO.Unsafe (unsafePerformIO )
2124import System.OsPath as OSP
2225import System.OsString.Internal.Types
2326
2427import qualified Data.ByteString as BS
2528import qualified Data.ByteString.Lazy as BSL
29+ import System.Posix.Types (CMode )
30+ #if MIN_VERSION_filepath(1, 5, 0)
31+ import qualified System.OsString as OSS
32+ #else
33+ import Data.Coerce
34+ #endif
2635
2736-- | Like 'openFile', but open the file in binary mode.
2837-- On Windows, reading a file in text mode (which is the default)
@@ -127,6 +136,56 @@ openFileWithCloseOnExec osfp iomode = augmentError "openFileWithCloseOnExec" osf
127136openExistingFileWithCloseOnExec :: OsPath -> IOMode -> IO Handle
128137openExistingFileWithCloseOnExec osfp iomode = augmentError " openExistingFileWithCloseOnExec" osfp $ withOpenFile' osfp iomode False True True pure False
129138
139+
140+ -- | The function creates a temporary file in ReadWrite mode.
141+ -- The created file isn\'t deleted automatically, so you need to delete it manually.
142+ --
143+ -- The file is created with permissions such that only the current
144+ -- user can read\/write it.
145+ --
146+ -- With some exceptions (see below), the file will be created securely
147+ -- in the sense that an attacker should not be able to cause
148+ -- openTempFile to overwrite another file on the filesystem using your
149+ -- credentials, by putting symbolic links (on Unix) in the place where
150+ -- the temporary file is to be created. On Unix the @O_CREAT@ and
151+ -- @O_EXCL@ flags are used to prevent this attack, but note that
152+ -- @O_EXCL@ is sometimes not supported on NFS filesystems, so if you
153+ -- rely on this behaviour it is best to use local filesystems only.
154+ --
155+ -- @since 0.1.3
156+ openTempFile :: OsPath -- ^ Directory in which to create the file
157+ -> OsString -- ^ File name template. If the template is \"foo.ext\" then
158+ -- the created file will be \"fooXXX.ext\" where XXX is some
159+ -- random number. Note that this should not contain any path
160+ -- separator characters. On Windows, the template prefix may
161+ -- be truncated to 3 chars, e.g. \"foobar.ext\" will be
162+ -- \"fooXXX.ext\".
163+ -> IO (OsPath , Handle )
164+ openTempFile tmp_dir template = openTempFile' " openTempFile" tmp_dir template False 0o600
165+
166+ -- | Like 'openTempFile', but opens the file in binary mode. See 'openBinaryFile' for more comments.
167+ --
168+ -- @since 0.1.3
169+ openBinaryTempFile :: OsPath -> OsString -> IO (OsPath , Handle )
170+ openBinaryTempFile tmp_dir template
171+ = openTempFile' " openBinaryTempFile" tmp_dir template True 0o600
172+
173+ -- | Like 'openTempFile', but uses the default file permissions
174+ --
175+ -- @since 0.1.3
176+ openTempFileWithDefaultPermissions :: OsPath -> OsString
177+ -> IO (OsPath , Handle )
178+ openTempFileWithDefaultPermissions tmp_dir template
179+ = openTempFile' " openTempFileWithDefaultPermissions" tmp_dir template False 0o666
180+
181+ -- | Like 'openBinaryTempFile', but uses the default file permissions
182+ --
183+ -- @since 0.1.3
184+ openBinaryTempFileWithDefaultPermissions :: OsPath -> OsString
185+ -> IO (OsPath , Handle )
186+ openBinaryTempFileWithDefaultPermissions tmp_dir template
187+ = openTempFile' " openBinaryTempFileWithDefaultPermissions" tmp_dir template True 0o666
188+
130189-- ---------------------------------------------------------------------------
131190-- Internals
132191
@@ -173,3 +232,29 @@ addFilePathToIOError fun fp ioe = unsafePerformIO $ do
173232augmentError :: String -> OsPath -> IO a -> IO a
174233augmentError str osfp = flip catchException (ioError . addFilePathToIOError str osfp)
175234
235+
236+ openTempFile' :: String -> OsPath -> OsString -> Bool -> CMode
237+ -> IO (OsPath , Handle )
238+ openTempFile' loc (OsString tmp_dir) template@ (OsString tmpl) binary mode
239+ | any_ (== OSP. pathSeparator) template
240+ = throwIO $ userError $ " openTempFile': Template string must not contain path separator characters: " ++ P. lenientDecode tmpl
241+ | otherwise = do
242+ (fp, hdl) <- P. findTempName (prefix, suffix) loc tmp_dir mode
243+ when binary $ hSetBinaryMode hdl True
244+ pure (OsString fp, hdl)
245+ where
246+ -- We split off the last extension, so we can use .foo.ext files
247+ -- for temporary files (hidden on Unix OSes). Unfortunately we're
248+ -- below filepath in the hierarchy here.
249+ (OsString prefix, OsString suffix) = OSP. splitExtension template
250+
251+ #if MIN_VERSION_filepath(1, 5, 0)
252+ any_ :: (OsChar -> Bool ) -> OsString -> Bool
253+ any_ = OSS. any
254+
255+ #else
256+ any_ :: (OsChar -> Bool ) -> OsString -> Bool
257+ any_ = coerce P. any_
258+
259+ #endif
260+
0 commit comments