summaryrefslogtreecommitdiff
path: root/Annex
diff options
context:
space:
mode:
Diffstat (limited to 'Annex')
-rw-r--r--Annex/Content.hs109
-rw-r--r--Annex/Exception.hs4
2 files changed, 83 insertions, 30 deletions
diff --git a/Annex/Content.hs b/Annex/Content.hs
index 7cd2fb561..98603914a 100644
--- a/Annex/Content.hs
+++ b/Annex/Content.hs
@@ -1,6 +1,6 @@
{- git-annex file content managing
-
- - Copyright 2010-2013 Joey Hess <joey@kitenet.net>
+ - Copyright 2010-2014 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
@@ -57,6 +57,10 @@ import Annex.Content.Direct
import Annex.ReplaceFile
import Annex.Exception
+#ifdef mingw32_HOST_OS
+import Utility.WinLock
+#endif
+
{- Checks if a given key's content is currently present. -}
inAnnex :: Key -> Annex Bool
inAnnex key = inAnnexCheck key $ liftIO . doesFileExist
@@ -90,60 +94,105 @@ inAnnex' isgood bad check key = withObjectLoc key checkindirect checkdirect
{- A safer check; the key's content must not only be present, but
- is not in the process of being removed. -}
inAnnexSafe :: Key -> Annex (Maybe Bool)
-inAnnexSafe = inAnnex' (fromMaybe False) (Just False) go
+inAnnexSafe key = inAnnex' (fromMaybe False) (Just False) go key
where
- go f = liftIO $ openforlock f >>= check
+ is_locked = Nothing
+ is_unlocked = Just True
+ is_missing = Just False
+
+ go contentfile = maybe (checkindirect contentfile) (checkdirect contentfile)
+ =<< contentLockFile key
+
#ifndef mingw32_HOST_OS
+ checkindirect f = liftIO $ openforlock f >>= check is_missing
+ {- In direct mode, the content file must exist, but
+ - the lock file often generally won't exist unless a removal is in
+ - process. This does not create the lock file, it only checks for
+ - it. -}
+ checkdirect contentfile lockfile = liftIO $
+ ifM (doesFileExist contentfile)
+ ( openforlock lockfile >>= check is_unlocked
+ , return is_missing
+ )
openforlock f = catchMaybeIO $
openFd f ReadOnly Nothing defaultFileFlags
-#else
- openforlock _ = return $ Just ()
-#endif
- check Nothing = return is_missing
-#ifndef mingw32_HOST_OS
- check (Just h) = do
+ check _ (Just h) = do
v <- getLock h (ReadLock, AbsoluteSeek, 0, 0)
closeFd h
return $ case v of
Just _ -> is_locked
Nothing -> is_unlocked
+ check def Nothing = return def
#else
- check (Just _) = return is_unlocked
-#endif
-#ifndef mingw32_HOST_OS
- is_locked = Nothing
+ checkindirect _ = return is_missing
+ {- In Windows, see if we can take a shared lock. If so,
+ - remove the lock file to clean up after ourselves. -}
+ checkdirect contentfile lockfile =
+ ifM (liftIO $ doesFileExist contentfile)
+ ( modifyContent lockfile $ liftIO $ do
+ v <- lockShared lockfile
+ case v of
+ Nothing -> return is_locked
+ Just lockhandle -> do
+ dropLock lockhandle
+ void $ tryIO $ nukeFile lockfile
+ return is_unlocked
+ , return is_missing
+ )
#endif
- is_unlocked = Just True
- is_missing = Just False
+
+{- Direct mode and especially Windows has to use a separate lock
+ - file from the content, since locking the actual content file
+ - would interfere with the user's use of it. -}
+contentLockFile :: Key -> Annex (Maybe FilePath)
+contentLockFile key = ifM isDirect
+ ( Just <$> calcRepo (gitAnnexContentLock key)
+ , return Nothing
+ )
{- Content is exclusively locked while running an action that might remove
- it. (If the content is not present, no locking is done.) -}
lockContent :: Key -> Annex a -> Annex a
-#ifndef mingw32_HOST_OS
lockContent key a = do
- file <- calcRepo $ gitAnnexLocation key
- bracketIO (openforlock file >>= lock) unlock (const a)
+ contentfile <- calcRepo $ gitAnnexLocation key
+ lockfile <- contentLockFile key
+ maybe noop setuplockfile lockfile
+ bracketAnnex (liftIO $ lock contentfile lockfile) (unlock lockfile) (const a)
where
- {- Since files are stored with the write bit disabled, have
+ alreadylocked = error "content is locked"
+ setuplockfile lockfile = modifyContent lockfile $
+ void $ liftIO $ tryIO $
+ writeFile lockfile ""
+ cleanuplockfile lockfile = modifyContent lockfile $
+ void $ liftIO $ tryIO $
+ nukeFile lockfile
+#ifndef mingw32_HOST_OS
+ lock contentfile Nothing = opencontentforlock contentfile >>= dolock
+ lock _ (Just lockfile) = openforlock lockfile >>= dolock . Just
+ {- Since content files are stored with the write bit disabled, have
- to fiddle with permissions to open for an exclusive lock. -}
- openforlock f = catchMaybeIO $ ifM (doesFileExist f)
+ opencontentforlock f = catchMaybeIO $ ifM (doesFileExist f)
( withModifiedFileMode f
(`unionFileModes` ownerWriteMode)
- open
- , open
+ (openforlock f)
+ , openforlock f
)
- where
- open = openFd f ReadWrite Nothing defaultFileFlags
- lock Nothing = return Nothing
- lock (Just fd) = do
+ openforlock f = openFd f ReadWrite Nothing defaultFileFlags
+ dolock Nothing = return Nothing
+ dolock (Just fd) = do
v <- tryIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
case v of
- Left _ -> error "content is locked"
+ Left _ -> alreadylocked
Right _ -> return $ Just fd
- unlock Nothing = noop
- unlock (Just l) = closeFd l
+ unlock mlockfile mfd = do
+ maybe noop cleanuplockfile mlockfile
+ liftIO $ maybe noop closeFd mfd
#else
-lockContent _key a = a -- no locking for Windows!
+ lock _ (Just lockfile) = maybe alreadylocked (return . Just) =<< lockExclusive lockfile
+ lock _ Nothing = return Nothing
+ unlock mlockfile mlockhandle = do
+ liftIO $ maybe noop dropLock mlockhandle
+ maybe noop cleanuplockfile mlockfile
#endif
{- Runs an action, passing it a temporary filename to get,
diff --git a/Annex/Exception.hs b/Annex/Exception.hs
index 91347583e..11613d51b 100644
--- a/Annex/Exception.hs
+++ b/Annex/Exception.hs
@@ -14,6 +14,7 @@
module Annex.Exception (
bracketIO,
+ bracketAnnex,
tryAnnex,
tryAnnexIO,
throwAnnex,
@@ -29,6 +30,9 @@ import Common.Annex
bracketIO :: IO v -> (v -> IO b) -> (v -> Annex a) -> Annex a
bracketIO setup cleanup = M.bracket (liftIO setup) (liftIO . cleanup)
+bracketAnnex :: Annex v -> (v -> Annex b) -> (v -> Annex a) -> Annex a
+bracketAnnex = M.bracket
+
{- try in the Annex monad -}
tryAnnex :: Annex a -> Annex (Either SomeException a)
tryAnnex = M.try