diff --git a/COPYING b/COPYING
new file mode 100644
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,23 @@
+Copyright (c) 2017
+Luis Pedro Coelho <luis@luispedro.org>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,2 @@
+Version 0.0.1.0 2017-05-29 by luispedro
+	* First version
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+# SafeIO: Haskell library for safe (atomic) IO
+
+This is a simple module, which enables writing in atomic mode. It
+implements the following 4 step procedure:
+
+1. Open a temporary file in the same directory as the final output.
+2. Write to this temporary file.
+3. Close and sync the file.
+4. Atomically rename the file to its final destination.
+
+## Example
+
+    import System.IO.SafeWrite
+    ...
+    main = do
+        withOutputFile "output.txt" $ \hout -> do
+            hPutStrLn hout "Hello World"
+
+
+## Author
+
+Luis Pedro Coelho [Email](mailto:luis@luispedro.org)
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/System/IO/SafeWrite.hs b/System/IO/SafeWrite.hs
new file mode 100644
--- /dev/null
+++ b/System/IO/SafeWrite.hs
@@ -0,0 +1,48 @@
+module System.IO.SafeWrite
+    ( withOutputFile
+    , syncFile
+    ) where
+
+import           System.FilePath (takeDirectory)
+import           System.Posix.IO (openFd, defaultFileFlags, closeFd, OpenMode(..))
+import           System.Posix.Unistd (fileSynchronise)
+import           Control.Exception (bracket, onException)
+import           System.IO (Handle, hClose, openTempFile)
+import           System.Directory (renameFile, removeFile)
+
+
+-- | Sync a file to disk
+--
+-- Only supported on Posix (patches with Windows support are welcome)
+syncFile :: FilePath -- ^ File to sync
+            -> IO ()
+syncFile fname = do
+    bracket (openFd fname ReadWrite Nothing defaultFileFlags)
+        closeFd
+        fileSynchronise
+    -- The code below will not work on Windows
+    bracket (openFd (takeDirectory fname) ReadOnly Nothing defaultFileFlags)
+        closeFd
+        fileSynchronise
+
+-- | Variation of 'withFile' for output files.
+--
+-- Output is written to a temporary file. Once the action has completed, this
+-- file is then sync'ed to disk (see |syncFile|) and renamed to its final
+-- destination. In Posix, this is an atomic operation. If an exception is
+-- raised, then the temporary output file will be deleted and not saved to
+-- disk. Thus, the result file will either contain the complete result or will
+-- be empty.
+withOutputFile ::
+            FilePath -- ^ Final desired file path
+            -> (Handle -> IO a) -- ^ action to execute
+            -> IO a
+withOutputFile finalname act = do
+    (tname, th) <- openTempFile (takeDirectory finalname) finalname
+    (do
+        r <- act th
+        hClose th
+        syncFile tname
+        renameFile tname finalname
+        return r) `onException` (hClose th >> removeFile tname)
+
diff --git a/System/IO/SafeWrite/Tests.hs b/System/IO/SafeWrite/Tests.hs
new file mode 100644
--- /dev/null
+++ b/System/IO/SafeWrite/Tests.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
+
+module Main where
+
+import Test.Framework.TH
+import Test.HUnit
+import Test.Framework.Providers.HUnit
+
+import Control.Exception (throwIO)
+import System.IO.Error (isDoesNotExistError, catchIOError)
+import System.Directory (doesFileExist, removeFile)
+import System.IO (hPutStrLn)
+
+import System.IO.SafeWrite
+
+main :: IO ()
+main = do
+    removeFileIfExists outname
+    $(defaultMainGenerator)
+
+removeFileIfExists :: FilePath -> IO ()
+removeFileIfExists fp = removeFile fp `catchIOError` ignoreDoesNotExistError
+    where
+        ignoreDoesNotExistError e
+                | isDoesNotExistError e = return ()
+                | otherwise = throwIO e
+
+outname :: FilePath
+outname = "testing-output.txt"
+
+case_create_output = do
+    withOutputFile outname $ flip hPutStrLn "Hello World"
+    (doesFileExist outname) >>= assertBool "Output file was not created"
+    removeFile outname
+
+case_not_create_on_exception = do
+    (withOutputFile outname $ \h -> do
+        hPutStrLn h "Hello World"
+        throwIO $ userError "Something bad happened") `catchIOError` \_ -> return ()
+    (not <$> doesFileExist outname) >>= assertBool "Output file was created despite exception being raised"
+
diff --git a/safeio.cabal b/safeio.cabal
new file mode 100644
--- /dev/null
+++ b/safeio.cabal
@@ -0,0 +1,45 @@
+name:               safeio
+version:            0.0.1.0
+synopsis:           Write output to disk atomically
+description:        This package implements utilities to perform atomic output
+		    so as to avoid the problem of partial intermediate files.
+category:           IO
+author:             Luis Pedro Coelho
+maintainer:         Luis Pedro Coelho
+license:            MIT
+license-file:       COPYING
+cabal-version:      >= 1.10
+build-type:         Simple
+bug-reports:        https://github.com/luispedro/safeio/issues
+extra-source-files: README.md ChangeLog
+
+library
+  default-language: Haskell2010
+  exposed-modules: System.IO.SafeWrite
+  ghc-options: -Wall
+  build-depends:
+    base > 4 && < 5,
+    directory,
+    filepath,
+    unix
+
+Test-Suite safeiotest
+  default-language: Haskell2010
+  type: exitcode-stdio-1.0
+  main-is: System/IO/SafeWrite/Tests.hs
+  other-modules: System.IO.SafeWrite
+  ghc-options: -Wall
+  hs-source-dirs: .
+  build-depends:
+    base > 4 && < 5,
+    directory,
+    filepath,
+    unix,
+    HUnit,
+    test-framework,
+    test-framework-hunit,
+    test-framework-th
+
+source-repository head
+  type: git
+  location: https://github.com/luispedro/safeio
