{-# OPTIONS_GHC -Wno-missing-signatures #-} module Unix ( fsync, fsyncExn , fdatasync, fdatasyncExn , ftruncate, ftruncateExn , mkdir, mkdirExn , preadBuf, preadBufExn , pread, preadExn , pwriteBuf, pwriteBufExn , pwrite, pwriteExn , pwriteFull, pwriteFullExn , readBuf, readBufExn , writeBuf, writeBufExn , read, readExn , remove, removeExn , rmdir, rmdirExn , write, writeExn , writeFull, writeFullExn , OpenFlag(..) , open, openExn , openat, openatExn , o_APPEND , o_CLOEXEC , o_CREAT , o_DIRECTORY , o_EXCL , o_NOFOLLOW , o_NONBLOCK , o_NDELAY , o_TRUNC , o_RDONLY , o_WRONLY , o_RDWR , close, closeExn -- * Re-exported for convenience , CString ) where import CString import Foreign.C.Error import Foreign.ForeignPtr import Unix.C import Unix.C.Errors import Unix.Errors import Zhp import qualified Data.ByteString as BS import qualified Data.ByteString.Internal as BS type EIO a = IO (Either Errno a) fsync :: Fd -> EIO () fsync fd = retryEINTR $ orErrno $ void $ c_fsync fd fsyncExn :: Fd -> IO () fsyncExn fd = throwIfErrno $ fsync fd fdatasync :: Fd -> EIO () fdatasync fd = retryEINTR $ orErrno $ void $ c_fdatasync fd fdatasyncExn :: Fd -> IO () fdatasyncExn fd = throwIfErrno $ fdatasync fd ftruncate :: Fd -> COff -> EIO () ftruncate fd off = retryEINTR $ orErrno $ void $ c_ftruncate fd off ftruncateExn :: Fd -> COff -> IO () ftruncateExn fd off = throwIfErrno $ ftruncate fd off preadBuf :: Fd -> Ptr Word8 -> CSize -> COff -> EIO CSsize preadBuf fd ptr sz off = retryEINTR $ orErrno $ c_pread fd ptr sz off preadBufExn :: Fd -> Ptr Word8 -> CSize -> COff -> IO CSsize preadBufExn fd ptr sz off = throwIfErrno $ preadBuf fd ptr sz off pread :: Fd -> CSize -> COff -> EIO BS.ByteString pread fd sz off = do fptr <- mallocForeignPtrBytes (fromIntegral sz) r <- withForeignPtr fptr $ \ptr -> preadBuf fd ptr sz off pure $! case r of Left e -> Left e Right v -> Right (BS.fromForeignPtr fptr 0 (fromIntegral v)) preadExn :: Fd -> CSize -> COff -> IO BS.ByteString preadExn fd sz off = throwIfErrno $ pread fd sz off pwriteBuf :: Fd -> Ptr Word8 -> CSize -> COff -> EIO CSsize pwriteBuf fd ptr sz off = retryEINTR $ orErrno $ c_pwrite fd ptr sz off pwriteBufExn :: Fd -> Ptr Word8 -> CSize -> COff -> IO CSsize pwriteBufExn fd ptr sz off = throwIfErrno $ pwriteBuf fd ptr sz off pwrite :: Fd -> BS.ByteString -> COff -> EIO CSsize pwrite fd bs off = let (fptr, foff, len) = BS.toForeignPtr bs in withForeignPtr fptr $ \ptr -> pwriteBuf fd (plusPtr ptr foff) (fromIntegral len) off pwriteExn :: Fd -> BS.ByteString -> COff -> IO CSsize pwriteExn fd bs off = throwIfErrno $ pwrite fd bs off pwriteFull :: Fd -> BS.ByteString -> COff -> EIO () pwriteFull fd bs off = do ret <- pwrite fd bs off case ret of Left e -> pure $ Left e Right v | fromIntegral v == BS.length bs -> pure $ Right () | otherwise -> pwriteFull fd (BS.drop (fromIntegral v) bs) (off + fromIntegral v) pwriteFullExn :: Fd -> BS.ByteString -> COff -> IO () pwriteFullExn fd bs off = throwIfErrno $ pwriteFull fd bs off readBuf :: Fd -> Ptr Word8 -> CSize -> EIO CSsize readBuf fd ptr sz = retryEINTR $ orErrno $ c_read fd ptr sz readBufExn :: Fd -> Ptr Word8 -> CSize -> IO CSsize readBufExn fd ptr sz = throwIfErrno $ readBuf fd ptr sz read :: Fd -> Int -> EIO BS.ByteString read fd sz = do fptr <- mallocForeignPtrBytes sz r <- withForeignPtr fptr $ \ptr -> readBuf fd ptr (fromIntegral sz) -- TODO(perf): should we trim the buffer to the size actually -- read? Maybe if it's below a certain size, -- possibly relative to the size of the buffer? pure $! case r of Left e -> Left e Right v -> Right (BS.fromForeignPtr fptr 0 (fromIntegral v)) readExn :: Fd -> Int -> IO BS.ByteString readExn fd sz = throwIfErrno $ read fd sz writeBuf :: Fd -> Ptr Word8 -> CSize -> EIO CSsize writeBuf fd ptr sz = retryEINTR $ orErrno $ c_write fd ptr sz writeBufExn :: Fd -> Ptr Word8 -> CSize -> IO CSsize writeBufExn fd ptr sz = throwIfErrno $ writeBuf fd ptr sz write :: Fd -> BS.ByteString -> EIO CSsize write fd bs = let (fptr, off, len) = BS.toForeignPtr bs in withForeignPtr fptr $ \ptr -> writeBuf fd (plusPtr ptr off) (fromIntegral len) writeExn :: Fd -> BS.ByteString -> IO CSsize writeExn fd bs = throwIfErrno $ write fd bs -- | Wrapper around write that makes sure the full bytestring is written, -- handling short writes from the underlying system call. writeFull :: Fd -> BS.ByteString -> EIO () writeFull fd bs = do ret <- write fd bs case ret of Left e -> pure $ Left e Right v | (fromIntegral v) == BS.length bs -> pure $ Right () | otherwise -> writeFull fd (BS.drop (fromIntegral v) bs) writeFullExn :: Fd -> BS.ByteString -> IO () writeFullExn fd bs = throwIfErrno $ writeFull fd bs remove :: CString -> EIO () remove fpath = useCStr fpath $ \path -> retryEINTR $ orErrno $ () <$ c_remove path removeExn :: CString -> IO () removeExn fpath = throwIfErrno $ remove fpath rmdir :: CString -> EIO () rmdir fpath = useCStr fpath $ \path -> retryEINTR $ orErrno $ () <$ c_rmdir path rmdirExn :: CString -> IO () rmdirExn fpath = throwIfErrno $ rmdir fpath mkdir :: CString -> CMode -> EIO () mkdir fpath mode = useCStr fpath $ \path -> retryEINTR $ orErrno $ () <$ c_mkdir path mode mkdirExn :: CString -> CMode -> IO () mkdirExn fpath mode = throwIfErrno $ mkdir fpath mode newtype OpenFlag = OpenFlag CInt instance Semigroup OpenFlag where (OpenFlag x) <> (OpenFlag y) = OpenFlag (x .|. y) open :: CString -> OpenFlag -> CMode -> EIO Fd open fpath (OpenFlag flag) mode = useCStr fpath $ \path -> retryEINTR $ orErrno $ c_open path flag mode openExn :: CString -> OpenFlag -> CMode -> IO Fd openExn path flag mode = throwIfErrno $ open path flag mode openat :: Fd -> CString -> OpenFlag -> CMode -> EIO Fd openat fd fpath (OpenFlag flag) mode = useCStr fpath $ \path -> retryEINTR $ orErrno $ c_openat fd path flag mode openatExn :: Fd -> CString -> OpenFlag -> CMode -> IO Fd openatExn fd path flag mode = throwIfErrno $ openat fd path flag mode o_APPEND = OpenFlag c_O_APPEND o_CLOEXEC = OpenFlag c_O_CLOEXEC o_CREAT = OpenFlag c_O_CREAT o_DIRECTORY = OpenFlag c_O_DIRECTORY o_EXCL = OpenFlag c_O_EXCL o_NOFOLLOW = OpenFlag c_O_NOFOLLOW o_NONBLOCK = OpenFlag c_O_NONBLOCK o_NDELAY = OpenFlag c_O_NDELAY o_TRUNC = OpenFlag c_O_TRUNC o_RDONLY = OpenFlag c_O_RDONLY o_WRONLY = OpenFlag c_O_WRONLY o_RDWR = OpenFlag c_O_RDWR close :: Fd -> EIO () close fd = orErrno $ void $ c_close fd -- We intentionally do not retryEINTR; posix is silent on whether -- the file descriptor will be closed after an EINTR return, but on -- at least Linux the answer is always yes, so we must not retry -- in case the file descriptor has been re-allocated by another thread. closeExn :: Fd -> IO () closeExn fd = throwIfErrno $ close fd