    {-# LANGUAGE CPP #-}
    {- |
       Module      :  System.Win32.HardLink
       Copyright   :  2013 shelarcy
       License     :  BSD-style

       Maintainer  :  shelarcy@gmail.com
       Stability   :  Provisional
       Portability :  Non-portable (Win32 API)

       Handling hard link using Win32 API. [NTFS only]

       Note: You should worry about file system type when use this module's function in your application:

         * NTFS only supprts this functionality.

         * ReFS doesn't support hard link currently.
    -}
    module System.Win32.HardLink
      ( createHardLink
      , createHardLink'
      ) where

    import System.Win32.File   ( LPSECURITY_ATTRIBUTES, failIfFalseWithRetry_ )
    import System.Win32.String ( LPCTSTR, withTString )
    import System.Win32.Types  ( BOOL, nullPtr )

    #include "windows_cconv.h"

    -- | NOTE: createHardLink is /flipped arguments/ to provide compatibility for Unix.

    --

    -- If you want to create hard link by Windows way, use 'createHardLink'' instead.

    createHardLink :: FilePath -- ^ Target file path

                   -> FilePath -- ^ Hard link name

                   -> IO ()
    createHardLink :: FilePath -> FilePath -> IO ()
    createHardLink = (FilePath -> FilePath -> IO ()) -> FilePath -> FilePath -> IO ()
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip FilePath -> FilePath -> IO ()
    createHardLink'

    createHardLink' :: FilePath -- ^ Hard link name

                    -> FilePath -- ^ Target file path

                    -> IO ()
    createHardLink' :: FilePath -> FilePath -> IO ()
    createHardLink' FilePath
    link FilePath
    target =
       FilePath -> (LPTSTR -> IO ()) -> IO ()
    forall a. FilePath -> (LPTSTR -> IO a) -> IO a
    withTString FilePath
    target ((LPTSTR -> IO ()) -> IO ()) -> (LPTSTR -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \LPTSTR
    c_target ->
       FilePath -> (LPTSTR -> IO ()) -> IO ()
    forall a. FilePath -> (LPTSTR -> IO a) -> IO a
    withTString FilePath
    link   ((LPTSTR -> IO ()) -> IO ()) -> (LPTSTR -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \LPTSTR
    c_link ->
            FilePath -> IO Bool -> IO ()
    failIfFalseWithRetry_ ([FilePath] -> FilePath
    unwords [FilePath
    "CreateHardLinkW",FilePath -> FilePath
    forall a. Show a => a -> FilePath
    show FilePath
    link,FilePath -> FilePath
    forall a. Show a => a -> FilePath
    show FilePath
    target]) (IO Bool -> IO ()) -> IO Bool -> IO ()
    forall a b. (a -> b) -> a -> b
    $
              LPTSTR -> LPTSTR -> LPSECURITY_ATTRIBUTES -> IO Bool
    c_CreateHardLink LPTSTR
    c_link LPTSTR
    c_target LPSECURITY_ATTRIBUTES
    forall a. Ptr a
    nullPtr

    foreign import WINDOWS_CCONV unsafe "windows.h CreateHardLinkW"
      c_CreateHardLink :: LPCTSTR -- ^ Hard link name

                       -> LPCTSTR -- ^ Target file path

                       -> LPSECURITY_ATTRIBUTES -- ^ This parameter is reserved. You should pass just /nullPtr/.

                       -> IO BOOL

    {-
    -- We plan to check file system type internally.

    -- We are thinking about API design, currently...
    data VolumeInformation = VolumeInformation
          { volumeName         :: String
          , volumeSerialNumber :: DWORD
          , maximumComponentLength :: DWORD
          , fileSystemFlags    :: DWORD
          , fileSystemName     :: String
          } deriving Show

    getVolumeInformation :: Maybe String -> IO VolumeInformation
    getVolumeInformation drive =
       maybeWith withTString drive $ \c_drive ->
       withTStringBufferLen 256    $ \(vnBuf, vnLen) ->
       alloca $ \serialNum ->
       alloca $ \maxLen ->
       alloca $ \fsFlags ->
       withTStringBufferLen 256 $ \(fsBuf, fsLen) -> do
           failIfFalse_ (unwords ["GetVolumeInformationW", drive]) $
             c_GetVolumeInformation c_drive vnBuf (fromIntegral vnLen)
                                    serialNum maxLen fsFlags
                                    fsBuf (fromIntegral fsLen)
           return VolumeInformation
             <*> peekTString vnBuf
             <*> peek serialNum
             <*> peek maxLen
             <*> peek fsFlags
             <*> peekTString fsBuf

    -- Which is better?
    getVolumeFileType :: String -> IO String
    getVolumeFileType drive = fileSystemName <$> getVolumeInformation drive

    getVolumeFileType :: String -> IO String
    getVolumeFileType drive =
       withTString drive        $ \c_drive ->
       withTStringBufferLen 256 $ \(buf, len) -> do
           failIfFalse_ (unwords ["GetVolumeInformationW", drive]) $
             c_GetVolumeInformation c_drive nullPtr 0 nullPtr nullPtr nullPtr buf (fromIntegral len)
           peekTString buf

    foreign import WINDOWS_CCONV unsafe "windows.h GetVolumeInformationW"
      c_GetVolumeInformation :: LPCTSTR -> LPTSTR -> DWORD -> LPDWORD -> LPDWORD -> LPDWORD -> LPTSTR -> DWORD -> IO BOOL
    -}