diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+## Zip 1.4.0
+
+* The “version made by” info inside archive now correctly sets Unix as the
+  OS that produced the archive when the library is compiled on Unix. This
+  allows other utilities such as `unzip` to read and correctly restore file
+  permissions. [Issue 62](https://github.com/mrkkrp/zip/issues/62).
+
+* Added the `Codec.Archive.Zip.Unix` module.
+
 ## Zip 1.3.2
 
 * Fix a bug where removing a temporary file failed in the prescence of
diff --git a/Codec/Archive/Zip.hs b/Codec/Archive/Zip.hs
--- a/Codec/Archive/Zip.hs
+++ b/Codec/Archive/Zip.hs
@@ -532,7 +532,10 @@
   -> ZipArchive ()
 deleteExtraField n s = addPending (I.DeleteExtraField n s)
 
--- | Set external file attributes.
+-- | Set external file attributes. This function can be used to set file
+-- permissions.
+--
+-- See also: "Codec.Archive.Zip.Unix".
 --
 -- @since 1.2.0
 
diff --git a/Codec/Archive/Zip/Internal.hs b/Codec/Archive/Zip/Internal.hs
--- a/Codec/Archive/Zip/Internal.hs
+++ b/Codec/Archive/Zip/Internal.hs
@@ -974,7 +974,7 @@
 -- specification.
 
 fromVersion :: Version -> Word16
-fromVersion v = fromIntegral (major * 10 + minor)
+fromVersion v = fromIntegral ((ZIP_OS `shiftL` 8) .|. (major * 10 + minor))
   where (major,minor) =
           case versionBranch v of
             v0:v1:_ -> (v0, v1)
diff --git a/Codec/Archive/Zip/Unix.hs b/Codec/Archive/Zip/Unix.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Archive/Zip/Unix.hs
@@ -0,0 +1,42 @@
+-- |
+-- Module      :  Codec.Archive.Zip.Unix
+-- Copyright   :  © 2016–present Mark Karpov
+-- License     :  BSD 3 clause
+--
+-- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- Unix specific functionality of zip archives.
+--
+-- @since 1.4.0
+
+module Codec.Archive.Zip.Unix
+  ( toFileMode
+  , fromFileMode )
+where
+
+import Data.Bits
+import Data.Word
+import System.Posix.Types (CMode (..))
+
+-- | Convert external attributes to the file info.
+--
+-- >>> toFileMode 2179792896
+-- 0o0755
+--
+-- @since 1.4.0
+
+toFileMode :: Word32 -> CMode
+toFileMode w = CMode $ (w `shiftR` 16) .&. 0x0fff
+
+-- | Convert external attributes to the file info. The function assumes a
+-- regular file and keeps DOS attributes untouched.
+--
+-- >>> fromFileMode 0o0755
+-- 2179792896
+--
+-- @since 1.4.0
+
+fromFileMode :: CMode -> Word32
+fromFileMode (CMode c) = (0o100000 .|. c) `shiftL` 16
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -8,9 +8,10 @@
 
 import Codec.Archive.Zip
 import Codec.Archive.Zip.CP437
+import Codec.Archive.Zip.Unix
 import Control.Monad
 import Control.Monad.IO.Class
-import Data.Bits (complement)
+import Data.Bits
 import Data.ByteString (ByteString)
 import Data.List (intercalate)
 import Data.Map (Map, (!))
@@ -18,13 +19,14 @@
 import Data.Text (Text)
 import Data.Time
 import Data.Version
+import Data.Word
 import System.Directory
 import System.FilePath ((</>))
 import System.IO
 import System.IO.Error (isDoesNotExistError)
 import System.IO.Temp
 import Test.Hspec
-import Test.QuickCheck
+import Test.QuickCheck hiding ((.&.))
 import qualified Data.ByteString         as B
 import qualified Data.ByteString.Builder as LB
 import qualified Data.ByteString.Lazy    as LB
@@ -52,6 +54,7 @@
   describe "unEntrySelector" unEntrySelectorSpec
   describe "getEntryName"    getEntryNameSpec
   describe "decodeCP437"     decodeCP437Spec
+  describe "fromFileMode"    fromFileModeSpec
   around withSandbox $ do
     describe "createArchive"      createArchiveSpec
     describe "withArchive"        withArchiveSpec
@@ -249,6 +252,15 @@
       c [0xa0..0xbf] "áíóúñÑªº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐"
       c [0xc0..0xdf] "└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀"
       c [0xe0..0xff] "αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ "
+
+fromFileModeSpec :: Spec
+fromFileModeSpec =
+  context "UNIX helpers" $ do
+    it "toFileMode . fromFileMode == id .&. 0x0fffff" . property $ \mode ->
+      (toFileMode . fromFileMode) (fromIntegral mode)
+        == fromIntegral (mode .&. (0x0fff :: Word16))
+    it "toFileMode == toFileMode . fromFileMode . toFileMode" . property $ \mode ->
+      toFileMode mode == (toFileMode.fromFileMode.toFileMode) mode
 
 ----------------------------------------------------------------------------
 -- Primitive editing/querying actions
diff --git a/zip.cabal b/zip.cabal
--- a/zip.cabal
+++ b/zip.cabal
@@ -1,5 +1,5 @@
 name:                 zip
-version:              1.3.2
+version:              1.4.0
 cabal-version:        1.18
 tested-with:          GHC==8.4.4, GHC==8.6.5, GHC==8.8.3
 license:              BSD3
@@ -49,6 +49,7 @@
     build-depends:    bzlib-conduit    >= 0.3     && < 0.4
   exposed-modules:    Codec.Archive.Zip
                     , Codec.Archive.Zip.CP437
+                    , Codec.Archive.Zip.Unix
   other-modules:      Codec.Archive.Zip.Internal
                     , Codec.Archive.Zip.Type
   if flag(dev)
@@ -62,6 +63,11 @@
   if !flag(disable-bzip2)
     cpp-options:      -DENABLE_BZIP2
   default-language:   Haskell2010
+
+  if os(windows)
+    cpp-options:      -DZIP_OS=0
+  else
+    cpp-options:      -DZIP_OS=3
 
 test-suite tests
   main-is:            Main.hs
