{- | Module : System.Linux.MemFd License : PublicDomain Maintainer : phlummox2@gmail.com Portability : non-portable (requires Linux) Create anonymous, memory-backed files with the Linux @memfd_create@ syscall. -} {-# LANGUAGE CApiFFI #-} {-# LANGUAGE CPP #-} module System.Linux.MemFd ( -- * memFdCreate memFdCreate , MemFdCreateFlag(..) -- * Low-level access -- -- | Access to the C-level functions and constants. , c_memfd_create , c_MFD_CLOEXEC , c_MFD_ALLOW_SEALING ) where import Foreign.C import System.Posix.Types import Data.Bits ( (.|.), Bits(..) ) import Data.List ( foldl' ) -- | Wrapper around -- @int memfd_create(const char *name, unsigned int flags)@ foreign import ccall unsafe "memfd_create" c_memfd_create :: CString -> CUInt -> IO Fd -- | Correspond to the unsigned int flags from @memfd.h@. data MemFdCreateFlag = CloseOnExec -- ^ MFD_CLOEXEC: close file descriptor if any @exec@ family functions are successfully called | AllowSealing -- ^ MFD_ALLOW_SEALING: allow file descriptor to be sealed using @fcntl@ deriving Eq -- CAPI calling convention lets us import even macros -- | MFD_CLOEXEC foreign import capi "memfd.h value MFD_CLOEXEC" c_MFD_CLOEXEC :: CUInt -- | MFD_ALLOW_SEALING foreign import capi "memfd.h value MFD_ALLOW_SEALING" c_MFD_ALLOW_SEALING :: CUInt memFdCreateFlagToInt :: MemFdCreateFlag -> CUInt memFdCreateFlagToInt op = case op of CloseOnExec -> c_MFD_CLOEXEC AllowSealing -> c_MFD_ALLOW_SEALING packMemFdCreateFlags :: [MemFdCreateFlag] -> CUInt packMemFdCreateFlags flags = foldl' (\acc flag -> memFdCreateFlagToInt flag .|. acc) 0 flags -- | @memFdCreate name flags@ creates -- an anonymous in-memory file and return a -- file descriptor referring to it. -- -- @name@ is used -- as a filename for debugging purposes, and will be displayed -- as the target of the corresponding symbolic link in the directory -- @\/proc\/self\/fd\/@. The displayed name is always prefixed with -- the string "@memfd:@". -- Names do not affect the behavior -- of the file descriptor, and multiple files can therefore -- have the same name without any side effects. -- -- The file behaves like a regular file, and so can be -- modified, truncated, memory-mapped, and so on. However, unlike a -- regular file, it lives in RAM and has a volatile backing storage. Once -- all -- OS references to the file are dropped, it is automatically released. -- -- A list of flags may be passed in @flags@. -- -- If the 'CloseOnExec' flag is passed, then the descriptor -- will be automatically and atomically closed -- when any of the @exec@ family functions succeed. -- -- If the 'AllowSealing' flag is passed, then the file can be -- /sealed/ using the @fcntl@ functions -- (see https://hackage.haskell.org/package/unix-fcntl -- for bindings to fcntl.) -- -- As a convenience, 'memFdSeal' is provided, which -- is a simplified interface to the @fcntl@ function. -- -- A path to the file is available via the @/proc@ fileystem, -- at @\/proc\/self\/fd\//myfd/@ (where "/myfd/" is the value of the file -- descriptor -- this file can be opened etc. like any other -- file using typical Haskell IO functions. -- -- Furthermore, as long as the 'CloseOnExec' flag -- is not passed, the file descriptor (and associated -- "@/proc@" path) will remain available to @fork@ed and -- @exec@ed child processes -- see the \"Examples\" directory -- for sample usage. -- -- Can also be used for "zero-trust" IPC -- see -- https://github.com/a-darwish/memfd-examples -- -- Example: -- -- >>> import System.Posix.IO (fdWrite) -- >>> fd <- memFdCreate "myfile" [] -- >>> _ <- fdWrite fd "The quality of mercy is not strained" -- >>> let fname = "/proc/self/fd/" ++ show fd -- >>> readFile fname >>= print -- "The quality of mercy is not strained" memFdCreate :: String -> [MemFdCreateFlag] -> IO Fd memFdCreate name flags = throwErrnoIfMinus1 "memFdCreate" $ withCString name $ \cStr -> c_memfd_create cStr $ packMemFdCreateFlags flags -- vim: syntax=haskell :