2016-03-03 16:49:54 +00:00
|
|
|
{- git lock files
|
|
|
|
-
|
|
|
|
- Copyright 2016 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
{-# LANGUAGE CPP #-}
|
|
|
|
|
|
|
|
module Git.LockFile where
|
|
|
|
|
|
|
|
import Common
|
|
|
|
|
|
|
|
#ifndef mingw32_HOST_OS
|
|
|
|
import System.Posix.Types
|
|
|
|
#else
|
|
|
|
import System.Win32.Types
|
|
|
|
import System.Win32.File
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef mingw32_HOST_OS
|
|
|
|
data LockHandle = LockHandle FilePath Fd
|
|
|
|
#else
|
|
|
|
data LockHandle = LockHandle FilePath HANDLE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{- Uses the same exclusive locking that git does.
|
|
|
|
- Throws an IO exception if the file is already locked.
|
|
|
|
-
|
|
|
|
- Note that git's locking method suffers from the problem that
|
|
|
|
- a dangling lock can be left if a process is terminated at the wrong
|
|
|
|
- time.
|
|
|
|
-}
|
|
|
|
openLock :: FilePath -> IO LockHandle
|
2016-03-03 17:05:00 +00:00
|
|
|
openLock lck = openLock' lck `catchNonAsync` lckerr
|
|
|
|
where
|
|
|
|
lckerr e = do
|
|
|
|
-- Same error message displayed by git.
|
|
|
|
whenM (doesFileExist lck) $
|
|
|
|
hPutStrLn stderr $ unlines
|
|
|
|
[ "fatal: Unable to create '" ++ lck ++ "': " ++ show e
|
|
|
|
, ""
|
|
|
|
, "If no other git process is currently running, this probably means a"
|
|
|
|
, "git process crashed in this repository earlier. Make sure no other git"
|
|
|
|
, "process is running and remove the file manually to continue."
|
|
|
|
]
|
|
|
|
throwM e
|
|
|
|
|
|
|
|
openLock' :: FilePath -> IO LockHandle
|
|
|
|
openLock' lck = do
|
2016-03-03 16:49:54 +00:00
|
|
|
#ifndef mingw32_HOST_OS
|
|
|
|
-- On unix, git simply uses O_EXCL
|
|
|
|
h <- openFd lck ReadWrite (Just 0O666)
|
|
|
|
(defaultFileFlags { exclusive = True })
|
|
|
|
setFdOption h CloseOnExec True
|
|
|
|
#else
|
|
|
|
-- It's not entirely clear how git manages locking on Windows,
|
|
|
|
-- since it's buried in the portability layer, and different
|
|
|
|
-- versions of git for windows use different portability layers.
|
|
|
|
-- But, we can be fairly sure that holding the lock file open on
|
|
|
|
-- windows is enough to prevent another process from opening it.
|
|
|
|
--
|
|
|
|
-- So, all that's needed is a way to open the file, that fails
|
|
|
|
-- if the file already exists. Using CreateFile with CREATE_NEW
|
|
|
|
-- accomplishes that.
|
|
|
|
h <- createFile lck gENERIC_WRITE fILE_SHARE_NONE Nothing
|
|
|
|
cREATE_NEW fILE_ATTRIBUTE_NORMAL Nothing
|
|
|
|
#endif
|
|
|
|
return (LockHandle lck h)
|
|
|
|
|
|
|
|
closeLock :: LockHandle -> IO ()
|
|
|
|
closeLock (LockHandle lck h) = do
|
|
|
|
#ifndef mingw32_HOST_OS
|
|
|
|
closeFd h
|
|
|
|
#else
|
|
|
|
closeHandle h
|
|
|
|
#endif
|
|
|
|
removeFile lck
|