diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,12 +1,16 @@
-## Changes in 0.2
-
-* Depend on Win32-errors for error handling.
-* More strict package dependencies
-
-## Changes in 0.1
-
-* 64-bit GHC is now supported.
-* Update cabal file to build on recent versions of GHC.
-* An internal foreign import is now being called with the "safe" option.
-* bug fixes. The deleteJunctionPoint function was failing to delete junction
-  points in many cases.
+## Changes in 0.2.1
+
+* Update constraints on 'text' library to support version 1.2.x
+
+## Changes in 0.2
+
+* Depend on Win32-errors for error handling.
+* More strict package dependencies
+
+## Changes in 0.1
+
+* 64-bit GHC is now supported.
+* Update cabal file to build on recent versions of GHC.
+* An internal foreign import is now being called with the "safe" option.
+* bug fixes. The deleteJunctionPoint function was failing to delete junction
+  points in many cases.
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2011-2013, Michael Steele
+Copyright (c) 2011-2015, Michael Steele
 Copyright (c) 2000, Mikael Nordell
 
 All rights reserved.
diff --git a/System/Win32/JunctionPoint.hs b/System/Win32/JunctionPoint.hs
--- a/System/Win32/JunctionPoint.hs
+++ b/System/Win32/JunctionPoint.hs
@@ -1,336 +1,336 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE OverloadedStrings #-}
-
-module System.Win32.JunctionPoint
-    ( createJunctionPoint
-    , deleteJunctionPoint
-    , getJunctionPointInfo
-    ) where
-
-import Control.Exception (bracket)
-import Data.Bits
-import Data.Char (chr)
-import Data.Text (Text)
-import qualified Data.Text as T
-import Data.Text.Foreign
-import Foreign
-import Foreign.C
-import System.Win32 hiding (createFile, failIf, failIfFalse_)
-import System.Win32.Error
-import System.Win32.Error.Foreign
-
-
-#include "windows_cconv.h"
-
--- Macro taken from winioctl.h
--- #define CTL_CODE( DeviceType, Function, Method, Access) ( \
---     (DWORD) ((DeviceType) << 16 | ((Access) << 14) | ((Function) << 2) \
---     | (Method))
-cTL_CODE :: DWORD -> DWORD -> DWORD -> DWORD -> DWORD
-cTL_CODE d f m a = d `shift` 16 .|. a `shift` 14 .|. f `shift` 2 .|. m
-
--- taken from winioctl.h
-fILE_DEVICE_FILE_SYSTEM :: DWORD
-fILE_DEVICE_FILE_SYSTEM = 0x00000009
-
--- taken from winioctl.h
-mETHOD_BUFFERED :: DWORD
-mETHOD_BUFFERED = 0
-
--- taken from winioctl.h
-fILE_ANY_ACCESS, fILE_SPECIAL_ACCESS :: DWORD
-fILE_ANY_ACCESS     = 0
-fILE_SPECIAL_ACCESS = 0
-
--- taken from winioctl.h
-fSCTL_SET_REPARSE_POINT :: DWORD
-fSCTL_SET_REPARSE_POINT = cTL_CODE fILE_DEVICE_FILE_SYSTEM 41
-    mETHOD_BUFFERED fILE_SPECIAL_ACCESS
-fSCTL_GET_REPARSE_POINT :: DWORD
-fSCTL_GET_REPARSE_POINT = cTL_CODE fILE_DEVICE_FILE_SYSTEM 42
-    mETHOD_BUFFERED fILE_ANY_ACCESS
-fSCTL_DELETE_REPARSE_POINT :: DWORD
-fSCTL_DELETE_REPARSE_POINT = cTL_CODE fILE_DEVICE_FILE_SYSTEM 43
-    mETHOD_BUFFERED fILE_SPECIAL_ACCESS
-
-iO_REPARSE_TAG_MOUNT_POINT :: DWORD
-iO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
-
---taken from MSDN's OpenFileById documentation
-fILE_FLAG_OPEN_REPARSE_POINT :: DWORD
-fILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
-
-mAXIMUM_REPARSE_DATA_BUFFER_SIZE :: Int
-mAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024
-
--- The first 3 fields of TMN_REPARSE_DATA_BUFFER are considered the
--- header. This is used in setReparsePoint.
-tMN_REPARSE_DATA_BUFFER_HEADER_SIZE :: DWORD
-tMN_REPARSE_DATA_BUFFER_HEADER_SIZE = 8
-
--- I've seen this defined as FIELD_OFFSET(REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer)
--- All the struct members up to the genericreparsebuffer add up to 24. I'm guessing that
--- the size-1 byte array does not add to this count.
-rEPARSE_GUID_DATA_BUFFER_HEADER_SIZE :: DWORD
-rEPARSE_GUID_DATA_BUFFER_HEADER_SIZE = 24
-
--- C structure. This is not documented in current versions of the
--- Windows SDK.
-
--- struct TMN_REPARSE_DATA_BUFFER
--- {
---     DWORD  ReparseTag;
---     WORD   ReparseDataLength;
---     WORD   Reserved;
---     WORD   SubstituteNameOffset;
---     WORD   SubstituteNameLength;
---     WORD   PrintNameOffset;
---     WORD   PrintNameLength;
---     WCHAR  PathBuffer[1];
--- };
-
-data TMN_REPARSE_DATA_BUFFER = TMN_REPARSE_DATA_BUFFER
-    { _reparseTag           :: !DWORD
-    -- Bytes in addition to first 8. This will be 12 + length of _pathBuffer
-    , _reparseDataLength    :: !WORD
-    , _reserved             :: !WORD
-    , _substituteNameOffset :: !WORD
-    , _substituteNameLength :: !WORD
-    , _printNameOffset      :: !WORD
-    , _printNameLength      :: !WORD
-    -- The C struct indicates a single-element array.
-    -- Actually, we need to allocate enough memory to
-    -- hold a string here.
-    , _pathBuffer :: !(Ptr CWchar)
-    }
-
-instance Storable TMN_REPARSE_DATA_BUFFER where
-  -- Storable does not allow the pokes of different sizes, so we're just
-  -- allocating the maximum size Microsoft documents.
-  sizeOf _    = mAXIMUM_REPARSE_DATA_BUFFER_SIZE
-  alignment _ = 1 -- no alignment? I don't know what this means.
-  peek ptr    = do
-    reparseTag           <- peek . castPtr $ ptr
-    reparseDataLength    <- castPtr ptr `peekByteOff` 4
-    reserved             <- castPtr ptr `peekByteOff` 6
-    substituteNameOffset <- castPtr ptr `peekByteOff` 8
-    substituteNameLength <- castPtr ptr `peekByteOff` 10
-    printNameOffset      <- castPtr ptr `peekByteOff` 12
-    printNameLength      <- castPtr ptr `peekByteOff` 14
-    let pathBuffer        = castPtr ptr `plusPtr`     16
-    return $ TMN_REPARSE_DATA_BUFFER reparseTag reparseDataLength reserved
-           substituteNameOffset substituteNameLength printNameOffset
-           printNameLength pathBuffer
-  poke ptr rdb = do
-    castPtr ptr `poke` _reparseTag rdb
-    castPtr ptr `pokeByteOff` 4  $ _reparseDataLength rdb
-    castPtr ptr `pokeByteOff` 6  $ _reserved rdb
-    castPtr ptr `pokeByteOff` 8  $ _substituteNameOffset rdb
-    castPtr ptr `pokeByteOff` 10 $ _substituteNameLength rdb
-    castPtr ptr `pokeByteOff` 12 $ _printNameOffset rdb
-    castPtr ptr `pokeByteOff` 14 $ _printNameLength rdb
-    strLen <- lengthArray0 0 $ _pathBuffer rdb
-    copyArray (ptr `plusPtr` 16) (_pathBuffer rdb) (strLen + 1)
-
--- This data structure is almost identical to TMN_REPARSE_DATA_BUFFER, but the
--- meaning of fields differ. Microsoft documents this structure. It is
--- used for querying existing reparse points.
-data REPARSE_GUID_DATA_BUFFER = REPARSE_GUID_DATA_BUFFER
-    { _rgdb_ReparseTag :: DWORD
-    -- MS specifies an odd structure, but a GUID is really just a 128-bit
-    -- value.
-    , _rgdb_GUID1             :: !DWORD
-    , _rgdb_GUID2             :: !DWORD
-    , _rgdb_GUID3             :: !DWORD
-    , _rgdb_GUID4             :: !DWORD
-    -- The C struct indicates a single-element array.
-    -- Actually, we need to allocate enough memory to
-    -- hold a string here.
-    , _rgdb_DataBuffer :: [BYTE]
-    }
-
-peekREPARSE_GUID_DATA_BUFFER :: Ptr REPARSE_GUID_DATA_BUFFER -> IO REPARSE_GUID_DATA_BUFFER
-peekREPARSE_GUID_DATA_BUFFER ptr = do
-    _rgdb_ReparseTag <- peek . castPtr $ ptr
-    dataLength       <- (castPtr ptr :: Ptr WORD) `peekByteOff` 4
-    -- reserved WORD at byte 6
-    _rgdb_GUID1      <- castPtr ptr `peekByteOff` 8
-    _rgdb_GUID2      <- castPtr ptr `peekByteOff` 12
-    _rgdb_GUID3      <- castPtr ptr `peekByteOff` 16
-    _rgdb_GUID4      <- castPtr ptr `peekByteOff` 20
-    dataBuffer       <- peekArray dataLength (castPtr ptr `plusPtr` 24)
-    return $ REPARSE_GUID_DATA_BUFFER _rgdb_ReparseTag
-           _rgdb_GUID1 _rgdb_GUID2 _rgdb_GUID3 _rgdb_GUID4 dataBuffer
-
-pokeREPARSE_GUID_DATA_BUFFER :: Ptr REPARSE_GUID_DATA_BUFFER -> REPARSE_GUID_DATA_BUFFER -> IO ()
-pokeREPARSE_GUID_DATA_BUFFER ptr rdb = do
-    castPtr ptr `poke` _rgdb_ReparseTag rdb
-    castPtr ptr `pokeByteOff` 4  $ (fromIntegral dataLength :: WORD)
-    castPtr ptr `pokeByteOff` 8  $ _rgdb_GUID1 rdb
-    castPtr ptr `pokeByteOff` 12 $ _rgdb_GUID2 rdb
-    castPtr ptr `pokeByteOff` 16 $ _rgdb_GUID3 rdb
-    castPtr ptr `pokeByteOff` 20 $ _rgdb_GUID4 rdb
-    pokeArray (castPtr ptr `plusPtr` 24) $ _rgdb_DataBuffer rdb
-    where
-      dataLength = length $ _rgdb_DataBuffer rdb
-
-withTMN_REPARSE_DATA_BUFFER :: Text
-    -> (Ptr TMN_REPARSE_DATA_BUFFER -> IO a) -> IO a
-withTMN_REPARSE_DATA_BUFFER dst f =
-    useAsPtr0 dst $ \c_dst ->
-    with (TMN_REPARSE_DATA_BUFFER
-          { _reparseTag           = iO_REPARSE_TAG_MOUNT_POINT
-          , _reparseDataLength    = dstLen + 12
-          , _reserved             = 0
-          , _substituteNameOffset = 0
-          , _substituteNameLength = dstLen
-          , _printNameOffset      = dstLen + 2
-          , _printNameLength      = 0
-          , _pathBuffer           = c_dst
-          }) f
-  where
-    dstLen = fromIntegral (T.length dst) * 2
-
-withREPARSE_GUID_DATA_BUFFER :: [BYTE]
-    -> (Ptr REPARSE_GUID_DATA_BUFFER -> IO a) -> IO a
-withREPARSE_GUID_DATA_BUFFER bx f =
-    allocaBytes (fromIntegral rEPARSE_GUID_DATA_BUFFER_HEADER_SIZE + length bx) $ \prgdb -> do
-    pokeREPARSE_GUID_DATA_BUFFER prgdb
-        $ REPARSE_GUID_DATA_BUFFER
-              { _rgdb_ReparseTag = iO_REPARSE_TAG_MOUNT_POINT
-              , _rgdb_GUID1 = 0
-              , _rgdb_GUID2 = 0
-              , _rgdb_GUID3 = 0
-              , _rgdb_GUID4 = 0
-              -- must be empty when deleting a junction point
-              , _rgdb_DataBuffer = bx
-              }
-    f prgdb
-
--- | Create a junction point between two folders on the same filesystem.
---
--- 'mountDir' should be the full file path to an empty folder. This
--- folder should be on a local filesystem.
---
--- 'destDir' should be the full file path to the junction point's target. The
--- target must be a folder on the same filesystem as its source. The path
--- should also be preceeded by \"\\??\\\". This indicates to Windows not to try
--- validating the path before using it.
---
--- >>> createJunctionPoint "c:\\Windows\\System32" "\\??\\c:\\System32"
-createJunctionPoint :: Text -- ^ mountDir
-                    -> Text -- ^ destDir
-                    -> IO ()
-createJunctionPoint mountDir destDir =
-    withTMN_REPARSE_DATA_BUFFER destDir $ \rdb ->
-    bracket (openReparseHandle mountDir) closeHandle $ \handle ->
-    setReparsePoint handle rdb
-
--- | This "Deletes" the junction point at the supplied path. An
--- empty folder will be left in its place on success. A 'Win32Exception'
--- exception will be thrown in the event of an error condition.
-deleteJunctionPoint :: Text -> IO ()
-deleteJunctionPoint dir =
-    bracket (openReparseHandle dir) closeHandle $ \handle -> do
-        deleteReparsePoint handle
-
--- | Discover the target of a junction point at the supplied path. A
--- 'Win32Exception' exception will be thrown if the target is either invalid
--- or not a junction point. The returned path will be prefixed by \"\\??\\\".
-getJunctionPointInfo :: Text -> IO Text
-getJunctionPointInfo dir =
-    bracket (openReparseHandle dir) closeHandle $ \handle ->
-    with (0 :: DWORD) $ \bytesReturned ->
-    withTMN_REPARSE_DATA_BUFFER (T.pack "") $ \pRdb -> do
-        deviceIoControl handle fSCTL_GET_REPARSE_POINT Nothing 0
-            (Just $ castPtr pRdb)
-            (fromIntegral mAXIMUM_REPARSE_DATA_BUFFER_SIZE)
-            (Just bytesReturned) Nothing
-        --not sure how to call IsReparseTagValid.
-        rdb <- peek pRdb
-        fromPtr0 $ (_pathBuffer rdb)
-
--- NTFS junction points are implemented as reparse points. Reparse points are
--- extra tagged information attached to filesystem objects. It is up to
--- application software such as the Windows Shell to detect the presence of
--- reparse points and act approprietly.
--- 
--- There are many types of reparse points. For the internal purposes of
--- this library I am pretending that junction points are the only kind of
--- reparse point.
-setReparsePoint :: HANDLE -> Ptr TMN_REPARSE_DATA_BUFFER -> IO ()
-setReparsePoint handle pRdb =
-    -- we don't care about bytesReturned
-    with (0 :: DWORD) $ \bytesReturned -> do
-        rdb <- peek pRdb
-        deviceIoControl handle fSCTL_SET_REPARSE_POINT
-            (Just $ castPtr pRdb)
-            (tMN_REPARSE_DATA_BUFFER_HEADER_SIZE +
-                (fromIntegral $ _reparseDataLength rdb))
-            Nothing 0 (Just bytesReturned) Nothing
-
--- 'deleteReparsePoint' will remove a reparse point pointed to by the
--- argument. An exception will be raised if the argument does not point to an
--- open reparse point, or possibly if the user does not have write attribute
--- permissions to the object.
-deleteReparsePoint :: HANDLE -> IO ()
-deleteReparsePoint handle =
-    -- When deleting reparse points the destination does not matter.
-    -- msdn docs refer to REPARSE_GUID_DATA_BUFFER for this instead.
-    withREPARSE_GUID_DATA_BUFFER [] $ \pRgdb ->
-    with (0 :: DWORD) $ \bytesReturned -> do
-        deviceIoControl handle fSCTL_DELETE_REPARSE_POINT
-            (Just $ castPtr pRgdb)
-            rEPARSE_GUID_DATA_BUFFER_HEADER_SIZE
-            Nothing 0 (Just bytesReturned) Nothing
-
--- Open a reparse point attached to the supplied folder. A `Win32Error` exception
--- will be thrown if the target does not exist, the user does not have read
--- permissions to it, or the target does not have a reparse point attached.
-openReparseHandle :: Text -> IO HANDLE
-openReparseHandle path = createFile path (gENERIC_READ .|. gENERIC_WRITE)
-    fILE_SHARE_NONE Nothing oPEN_EXISTING
-    (fILE_FLAG_BACKUP_SEMANTICS .|. fILE_FLAG_OPEN_REPARSE_POINT)
-    Nothing
-
--- Low-level filesystem manipulation function. Check documentation in the MSDN
--- library.
-deviceIoControl :: HANDLE -> DWORD -> Maybe LPVOID -> DWORD -> Maybe LPVOID
-    -> DWORD -> Maybe LPDWORD -> Maybe LPOVERLAPPED -> IO ()
-deviceIoControl hDevice dwIoControlCode lpInBuffer nInBufferSize
-    lpOutBuffer nOutBufferSize lpBytesReturned lpOverlapped =
-    failIfFalse_ "DeviceIoControl" $
-        c_DeviceIoControl hDevice dwIoControlCode
-            (maybe nullPtr id lpInBuffer) nInBufferSize
-            (maybe nullPtr id lpOutBuffer) nOutBufferSize
-            (maybe nullPtr id lpBytesReturned)
-            (maybe nullPtr id lpOverlapped)
-
-foreign import WINDOWS_CCONV "windows.h DeviceIoControl"
-    c_DeviceIoControl :: HANDLE -> DWORD -> LPVOID -> DWORD -> LPVOID
-        -> DWORD -> LPDWORD -> LPOVERLAPPED -> IO Bool
-
--- | Check MSDN documentation for what this action does.
-createFile :: Text -> AccessMode -> ShareMode -> Maybe LPSECURITY_ATTRIBUTES
-    -> CreateMode -> FileAttributeOrFlag -> Maybe HANDLE -> IO HANDLE
-createFile name access share mb_attr mode flag mb_h =
-    -- simply converting Text to a name does not add a null character
-    useAsPtr0 name $ \ c_name ->
-    failIf (== iNVALID_HANDLE_VALUE) "CreateFile" $
-    c_CreateFile c_name access share (maybePtr mb_attr) mode flag (maybePtr mb_h)
-
--- | useAsPtr returns a length and byte buffer, but all the win32 functions
--- rely on null termination.
-useAsPtr0 :: Text -> (Ptr CWchar -> IO a) -> IO a
-useAsPtr0 t f = useAsPtr (T.snoc t (chr 0x0)) $ \ str _ -> f  (castPtr str)
-
--- This traverses the string twice. Is there a faster way?
-fromPtr0 :: Ptr CWchar -> IO Text
-fromPtr0 ptr = do
-    -- length in 16-bit words.
-    len <- lengthArray0 0x0000 ptr'
-    -- no loss of precision here. I16 is a newtype wrapper around Int.
-    fromPtr ptr' $ fromIntegral len
-  where
-    ptr' :: Ptr Word16
-    ptr' = castPtr ptr
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module System.Win32.JunctionPoint
+    ( createJunctionPoint
+    , deleteJunctionPoint
+    , getJunctionPointInfo
+    ) where
+
+import Control.Exception (bracket)
+import Data.Bits
+import Data.Char (chr)
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Text.Foreign
+import Foreign
+import Foreign.C
+import System.Win32 hiding (createFile, failIf, failIfFalse_)
+import System.Win32.Error
+import System.Win32.Error.Foreign
+
+
+#include "windows_cconv.h"
+
+-- Macro taken from winioctl.h
+-- #define CTL_CODE( DeviceType, Function, Method, Access) ( \
+--     (DWORD) ((DeviceType) << 16 | ((Access) << 14) | ((Function) << 2) \
+--     | (Method))
+cTL_CODE :: DWORD -> DWORD -> DWORD -> DWORD -> DWORD
+cTL_CODE d f m a = d `shift` 16 .|. a `shift` 14 .|. f `shift` 2 .|. m
+
+-- taken from winioctl.h
+fILE_DEVICE_FILE_SYSTEM :: DWORD
+fILE_DEVICE_FILE_SYSTEM = 0x00000009
+
+-- taken from winioctl.h
+mETHOD_BUFFERED :: DWORD
+mETHOD_BUFFERED = 0
+
+-- taken from winioctl.h
+fILE_ANY_ACCESS, fILE_SPECIAL_ACCESS :: DWORD
+fILE_ANY_ACCESS     = 0
+fILE_SPECIAL_ACCESS = 0
+
+-- taken from winioctl.h
+fSCTL_SET_REPARSE_POINT :: DWORD
+fSCTL_SET_REPARSE_POINT = cTL_CODE fILE_DEVICE_FILE_SYSTEM 41
+    mETHOD_BUFFERED fILE_SPECIAL_ACCESS
+fSCTL_GET_REPARSE_POINT :: DWORD
+fSCTL_GET_REPARSE_POINT = cTL_CODE fILE_DEVICE_FILE_SYSTEM 42
+    mETHOD_BUFFERED fILE_ANY_ACCESS
+fSCTL_DELETE_REPARSE_POINT :: DWORD
+fSCTL_DELETE_REPARSE_POINT = cTL_CODE fILE_DEVICE_FILE_SYSTEM 43
+    mETHOD_BUFFERED fILE_SPECIAL_ACCESS
+
+iO_REPARSE_TAG_MOUNT_POINT :: DWORD
+iO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
+
+--taken from MSDN's OpenFileById documentation
+fILE_FLAG_OPEN_REPARSE_POINT :: DWORD
+fILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
+
+mAXIMUM_REPARSE_DATA_BUFFER_SIZE :: Int
+mAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024
+
+-- The first 3 fields of TMN_REPARSE_DATA_BUFFER are considered the
+-- header. This is used in setReparsePoint.
+tMN_REPARSE_DATA_BUFFER_HEADER_SIZE :: DWORD
+tMN_REPARSE_DATA_BUFFER_HEADER_SIZE = 8
+
+-- I've seen this defined as FIELD_OFFSET(REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer)
+-- All the struct members up to the genericreparsebuffer add up to 24. I'm guessing that
+-- the size-1 byte array does not add to this count.
+rEPARSE_GUID_DATA_BUFFER_HEADER_SIZE :: DWORD
+rEPARSE_GUID_DATA_BUFFER_HEADER_SIZE = 24
+
+-- C structure. This is not documented in current versions of the
+-- Windows SDK.
+
+-- struct TMN_REPARSE_DATA_BUFFER
+-- {
+--     DWORD  ReparseTag;
+--     WORD   ReparseDataLength;
+--     WORD   Reserved;
+--     WORD   SubstituteNameOffset;
+--     WORD   SubstituteNameLength;
+--     WORD   PrintNameOffset;
+--     WORD   PrintNameLength;
+--     WCHAR  PathBuffer[1];
+-- };
+
+data TMN_REPARSE_DATA_BUFFER = TMN_REPARSE_DATA_BUFFER
+    { _reparseTag           :: !DWORD
+    -- Bytes in addition to first 8. This will be 12 + length of _pathBuffer
+    , _reparseDataLength    :: !WORD
+    , _reserved             :: !WORD
+    , _substituteNameOffset :: !WORD
+    , _substituteNameLength :: !WORD
+    , _printNameOffset      :: !WORD
+    , _printNameLength      :: !WORD
+    -- The C struct indicates a single-element array.
+    -- Actually, we need to allocate enough memory to
+    -- hold a string here.
+    , _pathBuffer :: !(Ptr CWchar)
+    }
+
+instance Storable TMN_REPARSE_DATA_BUFFER where
+  -- Storable does not allow the pokes of different sizes, so we're just
+  -- allocating the maximum size Microsoft documents.
+  sizeOf _    = mAXIMUM_REPARSE_DATA_BUFFER_SIZE
+  alignment _ = 1 -- no alignment? I don't know what this means.
+  peek ptr    = do
+    reparseTag           <- peek . castPtr $ ptr
+    reparseDataLength    <- castPtr ptr `peekByteOff` 4
+    reserved             <- castPtr ptr `peekByteOff` 6
+    substituteNameOffset <- castPtr ptr `peekByteOff` 8
+    substituteNameLength <- castPtr ptr `peekByteOff` 10
+    printNameOffset      <- castPtr ptr `peekByteOff` 12
+    printNameLength      <- castPtr ptr `peekByteOff` 14
+    let pathBuffer        = castPtr ptr `plusPtr`     16
+    return $ TMN_REPARSE_DATA_BUFFER reparseTag reparseDataLength reserved
+           substituteNameOffset substituteNameLength printNameOffset
+           printNameLength pathBuffer
+  poke ptr rdb = do
+    castPtr ptr `poke` _reparseTag rdb
+    castPtr ptr `pokeByteOff` 4  $ _reparseDataLength rdb
+    castPtr ptr `pokeByteOff` 6  $ _reserved rdb
+    castPtr ptr `pokeByteOff` 8  $ _substituteNameOffset rdb
+    castPtr ptr `pokeByteOff` 10 $ _substituteNameLength rdb
+    castPtr ptr `pokeByteOff` 12 $ _printNameOffset rdb
+    castPtr ptr `pokeByteOff` 14 $ _printNameLength rdb
+    strLen <- lengthArray0 0 $ _pathBuffer rdb
+    copyArray (ptr `plusPtr` 16) (_pathBuffer rdb) (strLen + 1)
+
+-- This data structure is almost identical to TMN_REPARSE_DATA_BUFFER, but the
+-- meaning of fields differ. Microsoft documents this structure. It is
+-- used for querying existing reparse points.
+data REPARSE_GUID_DATA_BUFFER = REPARSE_GUID_DATA_BUFFER
+    { _rgdb_ReparseTag :: DWORD
+    -- MS specifies an odd structure, but a GUID is really just a 128-bit
+    -- value.
+    , _rgdb_GUID1             :: !DWORD
+    , _rgdb_GUID2             :: !DWORD
+    , _rgdb_GUID3             :: !DWORD
+    , _rgdb_GUID4             :: !DWORD
+    -- The C struct indicates a single-element array.
+    -- Actually, we need to allocate enough memory to
+    -- hold a string here.
+    , _rgdb_DataBuffer :: [BYTE]
+    }
+
+peekREPARSE_GUID_DATA_BUFFER :: Ptr REPARSE_GUID_DATA_BUFFER -> IO REPARSE_GUID_DATA_BUFFER
+peekREPARSE_GUID_DATA_BUFFER ptr = do
+    _rgdb_ReparseTag <- peek . castPtr $ ptr
+    dataLength       <- (castPtr ptr :: Ptr WORD) `peekByteOff` 4
+    -- reserved WORD at byte 6
+    _rgdb_GUID1      <- castPtr ptr `peekByteOff` 8
+    _rgdb_GUID2      <- castPtr ptr `peekByteOff` 12
+    _rgdb_GUID3      <- castPtr ptr `peekByteOff` 16
+    _rgdb_GUID4      <- castPtr ptr `peekByteOff` 20
+    dataBuffer       <- peekArray dataLength (castPtr ptr `plusPtr` 24)
+    return $ REPARSE_GUID_DATA_BUFFER _rgdb_ReparseTag
+           _rgdb_GUID1 _rgdb_GUID2 _rgdb_GUID3 _rgdb_GUID4 dataBuffer
+
+pokeREPARSE_GUID_DATA_BUFFER :: Ptr REPARSE_GUID_DATA_BUFFER -> REPARSE_GUID_DATA_BUFFER -> IO ()
+pokeREPARSE_GUID_DATA_BUFFER ptr rdb = do
+    castPtr ptr `poke` _rgdb_ReparseTag rdb
+    castPtr ptr `pokeByteOff` 4  $ (fromIntegral dataLength :: WORD)
+    castPtr ptr `pokeByteOff` 8  $ _rgdb_GUID1 rdb
+    castPtr ptr `pokeByteOff` 12 $ _rgdb_GUID2 rdb
+    castPtr ptr `pokeByteOff` 16 $ _rgdb_GUID3 rdb
+    castPtr ptr `pokeByteOff` 20 $ _rgdb_GUID4 rdb
+    pokeArray (castPtr ptr `plusPtr` 24) $ _rgdb_DataBuffer rdb
+    where
+      dataLength = length $ _rgdb_DataBuffer rdb
+
+withTMN_REPARSE_DATA_BUFFER :: Text
+    -> (Ptr TMN_REPARSE_DATA_BUFFER -> IO a) -> IO a
+withTMN_REPARSE_DATA_BUFFER dst f =
+    useAsPtr0 dst $ \c_dst ->
+    with (TMN_REPARSE_DATA_BUFFER
+          { _reparseTag           = iO_REPARSE_TAG_MOUNT_POINT
+          , _reparseDataLength    = dstLen + 12
+          , _reserved             = 0
+          , _substituteNameOffset = 0
+          , _substituteNameLength = dstLen
+          , _printNameOffset      = dstLen + 2
+          , _printNameLength      = 0
+          , _pathBuffer           = c_dst
+          }) f
+  where
+    dstLen = fromIntegral (T.length dst) * 2
+
+withREPARSE_GUID_DATA_BUFFER :: [BYTE]
+    -> (Ptr REPARSE_GUID_DATA_BUFFER -> IO a) -> IO a
+withREPARSE_GUID_DATA_BUFFER bx f =
+    allocaBytes (fromIntegral rEPARSE_GUID_DATA_BUFFER_HEADER_SIZE + length bx) $ \prgdb -> do
+    pokeREPARSE_GUID_DATA_BUFFER prgdb
+        $ REPARSE_GUID_DATA_BUFFER
+              { _rgdb_ReparseTag = iO_REPARSE_TAG_MOUNT_POINT
+              , _rgdb_GUID1 = 0
+              , _rgdb_GUID2 = 0
+              , _rgdb_GUID3 = 0
+              , _rgdb_GUID4 = 0
+              -- must be empty when deleting a junction point
+              , _rgdb_DataBuffer = bx
+              }
+    f prgdb
+
+-- | Create a junction point between two folders on the same filesystem.
+--
+-- 'mountDir' should be the full file path to an empty folder. This
+-- folder should be on a local filesystem.
+--
+-- 'destDir' should be the full file path to the junction point's target. The
+-- target must be a folder on the same filesystem as its source. The path
+-- should also be preceeded by \"\\??\\\". This indicates to Windows not to try
+-- validating the path before using it.
+--
+-- >>> createJunctionPoint "c:\\Windows\\System32" "\\??\\c:\\System32"
+createJunctionPoint :: Text -- ^ mountDir
+                    -> Text -- ^ destDir
+                    -> IO ()
+createJunctionPoint mountDir destDir =
+    withTMN_REPARSE_DATA_BUFFER destDir $ \rdb ->
+    bracket (openReparseHandle mountDir) closeHandle $ \handle ->
+    setReparsePoint handle rdb
+
+-- | This "Deletes" the junction point at the supplied path. An
+-- empty folder will be left in its place on success. A 'Win32Exception'
+-- exception will be thrown in the event of an error condition.
+deleteJunctionPoint :: Text -> IO ()
+deleteJunctionPoint dir =
+    bracket (openReparseHandle dir) closeHandle $ \handle -> do
+        deleteReparsePoint handle
+
+-- | Discover the target of a junction point at the supplied path. A
+-- 'Win32Exception' exception will be thrown if the target is either invalid
+-- or not a junction point. The returned path will be prefixed by \"\\??\\\".
+getJunctionPointInfo :: Text -> IO Text
+getJunctionPointInfo dir =
+    bracket (openReparseHandle dir) closeHandle $ \handle ->
+    with (0 :: DWORD) $ \bytesReturned ->
+    withTMN_REPARSE_DATA_BUFFER (T.pack "") $ \pRdb -> do
+        deviceIoControl handle fSCTL_GET_REPARSE_POINT Nothing 0
+            (Just $ castPtr pRdb)
+            (fromIntegral mAXIMUM_REPARSE_DATA_BUFFER_SIZE)
+            (Just bytesReturned) Nothing
+        --not sure how to call IsReparseTagValid.
+        rdb <- peek pRdb
+        fromPtr0 $ (_pathBuffer rdb)
+
+-- NTFS junction points are implemented as reparse points. Reparse points are
+-- extra tagged information attached to filesystem objects. It is up to
+-- application software such as the Windows Shell to detect the presence of
+-- reparse points and act approprietly.
+-- 
+-- There are many types of reparse points. For the internal purposes of
+-- this library I am pretending that junction points are the only kind of
+-- reparse point.
+setReparsePoint :: HANDLE -> Ptr TMN_REPARSE_DATA_BUFFER -> IO ()
+setReparsePoint handle pRdb =
+    -- we don't care about bytesReturned
+    with (0 :: DWORD) $ \bytesReturned -> do
+        rdb <- peek pRdb
+        deviceIoControl handle fSCTL_SET_REPARSE_POINT
+            (Just $ castPtr pRdb)
+            (tMN_REPARSE_DATA_BUFFER_HEADER_SIZE +
+                (fromIntegral $ _reparseDataLength rdb))
+            Nothing 0 (Just bytesReturned) Nothing
+
+-- 'deleteReparsePoint' will remove a reparse point pointed to by the
+-- argument. An exception will be raised if the argument does not point to an
+-- open reparse point, or possibly if the user does not have write attribute
+-- permissions to the object.
+deleteReparsePoint :: HANDLE -> IO ()
+deleteReparsePoint handle =
+    -- When deleting reparse points the destination does not matter.
+    -- msdn docs refer to REPARSE_GUID_DATA_BUFFER for this instead.
+    withREPARSE_GUID_DATA_BUFFER [] $ \pRgdb ->
+    with (0 :: DWORD) $ \bytesReturned -> do
+        deviceIoControl handle fSCTL_DELETE_REPARSE_POINT
+            (Just $ castPtr pRgdb)
+            rEPARSE_GUID_DATA_BUFFER_HEADER_SIZE
+            Nothing 0 (Just bytesReturned) Nothing
+
+-- Open a reparse point attached to the supplied folder. A `Win32Error` exception
+-- will be thrown if the target does not exist, the user does not have read
+-- permissions to it, or the target does not have a reparse point attached.
+openReparseHandle :: Text -> IO HANDLE
+openReparseHandle path = createFile path (gENERIC_READ .|. gENERIC_WRITE)
+    fILE_SHARE_NONE Nothing oPEN_EXISTING
+    (fILE_FLAG_BACKUP_SEMANTICS .|. fILE_FLAG_OPEN_REPARSE_POINT)
+    Nothing
+
+-- Low-level filesystem manipulation function. Check documentation in the MSDN
+-- library.
+deviceIoControl :: HANDLE -> DWORD -> Maybe LPVOID -> DWORD -> Maybe LPVOID
+    -> DWORD -> Maybe LPDWORD -> Maybe LPOVERLAPPED -> IO ()
+deviceIoControl hDevice dwIoControlCode lpInBuffer nInBufferSize
+    lpOutBuffer nOutBufferSize lpBytesReturned lpOverlapped =
+    failIfFalse_ "DeviceIoControl" $
+        c_DeviceIoControl hDevice dwIoControlCode
+            (maybe nullPtr id lpInBuffer) nInBufferSize
+            (maybe nullPtr id lpOutBuffer) nOutBufferSize
+            (maybe nullPtr id lpBytesReturned)
+            (maybe nullPtr id lpOverlapped)
+
+foreign import WINDOWS_CCONV "windows.h DeviceIoControl"
+    c_DeviceIoControl :: HANDLE -> DWORD -> LPVOID -> DWORD -> LPVOID
+        -> DWORD -> LPDWORD -> LPOVERLAPPED -> IO Bool
+
+-- | Check MSDN documentation for what this action does.
+createFile :: Text -> AccessMode -> ShareMode -> Maybe LPSECURITY_ATTRIBUTES
+    -> CreateMode -> FileAttributeOrFlag -> Maybe HANDLE -> IO HANDLE
+createFile name access share mb_attr mode flag mb_h =
+    -- simply converting Text to a name does not add a null character
+    useAsPtr0 name $ \ c_name ->
+    failIf (== iNVALID_HANDLE_VALUE) "CreateFile" $
+    c_CreateFile c_name access share (maybePtr mb_attr) mode flag (maybePtr mb_h)
+
+-- | useAsPtr returns a length and byte buffer, but all the win32 functions
+-- rely on null termination.
+useAsPtr0 :: Text -> (Ptr CWchar -> IO a) -> IO a
+useAsPtr0 t f = useAsPtr (T.snoc t (chr 0x0)) $ \ str _ -> f  (castPtr str)
+
+-- This traverses the string twice. Is there a faster way?
+fromPtr0 :: Ptr CWchar -> IO Text
+fromPtr0 ptr = do
+    -- length in 16-bit words.
+    len <- lengthArray0 0x0000 ptr'
+    -- no loss of precision here. I16 is a newtype wrapper around Int.
+    fromPtr ptr' $ fromIntegral len
+  where
+    ptr' :: Ptr Word16
+    ptr' = castPtr ptr
diff --git a/Win32-junction-point.cabal b/Win32-junction-point.cabal
--- a/Win32-junction-point.cabal
+++ b/Win32-junction-point.cabal
@@ -1,59 +1,59 @@
-Name:          Win32-junction-point
-Version:       0.2
-Synopsis:      Support for manipulating NTFS junction points.
-Description:
-  This package provides the ability to manipulate NTFS junction points as
-  supported by Windows 2000 and above. Junction points, along with NTFS hard
-  links and NTFS symbolic links, are a type of symbolic link that can be made
-  between folders existing on the same filesystem. Please read Microsoft
-  KB205524 [1] for more information on junction points.
-  .
-  Junction points have always been left undocumented in the Win32 SDK. The
-  Windows 2000 Resource Kit came with a command-line utility named linkd.exe
-  to work with them. Later, Mark Russinovich of SysInternals distributed a
-  replacement utility named Junction [2] which accomplished the same thing.
-  .
-  This source code is based on an article [3] and C++ library [4] written by
-  Mike Nordell at codeproject.com.
-  .
-  It is against Microsoft's recommendation to make use of undocumented API
-  features. Use this library at your own risk.
-  .
-  (1) <http://support.microsoft.com/?kbid=205524>
-  .
-  (2) <http://technet.microsoft.com/en-us/sysinternals/bb896768>
-  .
-  (3) <http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=194>
-  .
-  (4) <http://www.codeproject.com/KB/winsdk/junctionpoints.aspx>
-License:       BSD3
-License-file:  LICENSE
-Author:        Michael Steele
-Maintainer:    mikesteele81@gmail.com
-Copyright:     Copyright (c) 2011-2014, Michael Steele. Copyright (c) 2000, Mikael Nordell
-Category:      System
-Build-type:    Simple
-Cabal-version: >=1.16
-Homepage:      http://github.com/mikesteele81/Win32-junction-point
-bug-reports:   http://github.com/mikesteele81/win32-junction-point/issues
-Stability:     experimental
-tested-with:   GHC==7.8.2
-extra-source-files:
-    include/windows_cconv.h
-    ChangeLog
-
-source-repository head
-  type: git
-  location: git://github.com/mikesteele81/win32-junction-point.git
-
-Library
-  default-language: Haskell2010
-  ghc-options: -Wall -funbox-strict-fields
-  cc-options:  -fno-strict-aliasing
-  Exposed-modules: System.Win32.JunctionPoint
-
-  Build-depends: base  >= 4.0 && < 4.8
-               , text  >= 0.8 && < 1.2
-               , Win32 >= 2.0 && < 2.4
-               , Win32-errors >= 0.2 && < 0.3
-  include-dirs: include
+Name:          Win32-junction-point
+Version:       0.2.1
+Synopsis:      Support for manipulating NTFS junction points.
+Description:
+  This package provides the ability to manipulate NTFS junction points as
+  supported by Windows 2000 and above. Junction points, along with NTFS hard
+  links and NTFS symbolic links, are a type of symbolic link that can be made
+  between folders existing on the same filesystem. Please read Microsoft
+  KB205524 [1] for more information on junction points.
+  .
+  Junction points have always been left undocumented in the Win32 SDK. The
+  Windows 2000 Resource Kit came with a command-line utility named linkd.exe
+  to work with them. Later, Mark Russinovich of SysInternals distributed a
+  replacement utility named Junction [2] which accomplished the same thing.
+  .
+  This source code is based on an article [3] and C++ library [4] written by
+  Mike Nordell at codeproject.com.
+  .
+  It is against Microsoft's recommendation to make use of undocumented API
+  features. Use this library at your own risk.
+  .
+  (1) <http://support.microsoft.com/?kbid=205524>
+  .
+  (2) <http://technet.microsoft.com/en-us/sysinternals/bb896768>
+  .
+  (3) <http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=194>
+  .
+  (4) <http://www.codeproject.com/KB/winsdk/junctionpoints.aspx>
+License:       BSD3
+License-file:  LICENSE
+Author:        Michael Steele
+Maintainer:    mikesteele81@gmail.com
+Copyright:     Copyright (c) 2011-2015, Michael Steele. Copyright (c) 2000, Mikael Nordell
+Category:      System
+Build-type:    Simple
+Cabal-version: >=1.16
+Homepage:      http://github.com/mikesteele81/Win32-junction-point
+bug-reports:   http://github.com/mikesteele81/win32-junction-point/issues
+Stability:     experimental
+tested-with:   GHC==7.8.3
+extra-source-files:
+    include/windows_cconv.h
+    ChangeLog
+
+source-repository head
+  type: git
+  location: git://github.com/mikesteele81/win32-junction-point.git
+
+Library
+  default-language: Haskell2010
+  ghc-options: -Wall -funbox-strict-fields
+  cc-options:  -fno-strict-aliasing
+  Exposed-modules: System.Win32.JunctionPoint
+
+  Build-depends: base  >= 4.0 && < 4.8
+               , text  >= 0.8 && < 1.3
+               , Win32 >= 2.0 && < 2.4
+               , Win32-errors >= 0.2 && < 0.3
+  include-dirs: include
