@@ -4,6 +4,8 @@ module System.Nix.Nar.Effects
44 ( NarEffects (.. )
55 , narEffectsIO
66 , IsExecutable (.. )
7+ , isExecutable
8+ , setExecutable
79 ) where
810
911import Control.Monad.Trans.Control (MonadBaseControl )
@@ -19,10 +21,21 @@ import qualified Data.ByteString
1921import qualified Data.ByteString.Lazy as Bytes.Lazy
2022import qualified System.Directory as Directory
2123import System.Posix.Files ( createSymbolicLink
24+ , fileMode
2225 , fileSize
26+ , FileStatus
2327 , getFileStatus
28+ , getSymbolicLinkStatus
29+ , groupExecuteMode
30+ , intersectFileModes
2431 , isDirectory
32+ , isRegularFile
33+ , nullFileMode
34+ , otherExecuteMode
35+ , ownerExecuteMode
2536 , readSymbolicLink
37+ , setFileMode
38+ , unionFileModes
2639 )
2740import qualified System.IO as IO
2841import qualified Control.Exception.Lifted as Exception.Lifted
@@ -59,13 +72,13 @@ narEffectsIO = NarEffects {
5972 narReadFile = liftIO . Bytes.Lazy. readFile
6073 , narWriteFile = \ f e c -> liftIO $ do
6174 Bytes.Lazy. writeFile f c
62- p <- Directory. getPermissions f
63- Directory. setPermissions f (p { Directory. executable = e == Executable })
75+ Control.Monad. when (e == Executable ) $
76+ setExecutable f
6477 , narStreamFile = streamStringOutIO
6578 , narListDir = liftIO . Directory. listDirectory
6679 , narCreateDir = liftIO . Directory. createDirectory
6780 , narCreateLink = \ f -> liftIO . createSymbolicLink f
68- , narIsExec = liftIO . ( fmap (bool NonExecutable Executable . Directory. executable)) . Directory. getPermissions
81+ , narIsExec = liftIO . fmap (bool NonExecutable Executable . isExecutable) . getSymbolicLinkStatus
6982 , narIsDir = fmap isDirectory . liftIO . getFileStatus
7083 , narIsSymLink = liftIO . Directory. pathIsSymbolicLink
7184 , narFileSize = fmap (fromIntegral . fileSize) . liftIO . getFileStatus
@@ -102,10 +115,26 @@ streamStringOutIO f executable getChunk =
102115 liftIO $ Data.ByteString. hPut handle c
103116 go handle
104117 updateExecutablePermissions =
105- Control.Monad. when (executable == Executable ) $ do
106- p <- Directory. getPermissions f
107- Directory. setPermissions f (p { Directory. executable = True })
118+ Control.Monad. when (executable == Executable ) $
119+ setExecutable f
108120 cleanupException (e :: Exception.Lifted. SomeException ) = do
109121 liftIO $ Directory. removeFile f
110122 Control.Monad. fail $
111123 " Failed to stream string to " <> f <> " : " <> show e
124+
125+ -- | Check whether the file is executable by the owner.
126+ isExecutable :: FileStatus -> Bool
127+ isExecutable st =
128+ isRegularFile st
129+ && fileMode st `intersectFileModes` ownerExecuteMode /= nullFileMode
130+
131+ -- | Set the file to be executable by the owner, group, and others.
132+ setExecutable :: FilePath -> IO ()
133+ setExecutable f = do
134+ st <- getSymbolicLinkStatus f
135+ let p =
136+ fileMode st
137+ `unionFileModes` ownerExecuteMode
138+ `unionFileModes` groupExecuteMode
139+ `unionFileModes` otherExecuteMode
140+ setFileMode f p
0 commit comments