- the content is not present, because the content file is not always
- the file that is locked. -}
lockContentUsing :: ContentLocker -> Key -> Annex a -> Annex a -> Annex a
-lockContentUsing contentlocker key fallback a = do
+lockContentUsing contentlocker key fallback a = withContentLockFile key $ \mlockfile -> do
contentfile <- calcRepo (gitAnnexLocation key)
- mlockfile <- contentLockFile key
let (locker, sharedtoexclusive) = contentlocker contentfile mlockfile
bracket
(lock locker mlockfile)
{- git-annex object content presence
-
- - Copyright 2010-2021 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2022 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
isUnmodified,
isUnmodified',
isUnmodifiedCheap,
- contentLockFile,
+ withContentLockFile,
) where
import Annex.Content.Presence.LowLevel
import Annex.Common
import qualified Annex
import Annex.LockPool
+import Annex.LockFile
import Annex.Version
+import Types.RepoVersion
import qualified Database.Keys
import Annex.InodeSentinal
import Utility.InodeCache
is_unlocked = Just True
is_missing = Just False
- go contentfile = flip checklock contentfile =<< contentLockFile key
+ go contentfile = withContentLockFile key $ flip checklock contentfile
#ifndef mingw32_HOST_OS
checklock Nothing contentfile = checkOr is_missing contentfile
)
#endif
-contentLockFile :: Key -> Annex (Maybe RawFilePath)
+{- Runs an action with the lock file to use to lock a key's content.
+ - When the content file itself should be locked, runs the action with
+ - Nothing.
+ -
+ - In v9 and below, while the action is running, a shared lock is held of the
+ - gitAnnexContentLockLock. That prevents the v10 upgrade, which changes how
+ - content locking works, from running at the same time as content is locked
+ - using the old method.
+ -}
+withContentLockFile :: Key -> (Maybe RawFilePath -> Annex a) -> Annex a
+withContentLockFile k a = do
+ v <- getVersion
+ let go = contentLockFile k v >>= a
+ if versionNeedsWritableContentFiles v
+ then withSharedLock gitAnnexContentLockLock go
+ else go
+
+contentLockFile :: Key -> Maybe RepoVersion -> Annex (Maybe RawFilePath)
#ifndef mingw32_HOST_OS
{- Older versions of git-annex locked content files themselves, but newer
- versions use a separate lock file, to better support repos shared
- amoung users in eg a group. -}
-contentLockFile key = ifM (versionNeedsWritableContentFiles <$> getVersion)
- ( pure Nothing
- , Just <$> calcRepo (gitAnnexContentLock key)
- )
+contentLockFile key v
+ | versionNeedsWritableContentFiles v = pure Nothing
+ | otherwise = Just <$> calcRepo (gitAnnexContentLock key)
#else
{- Windows always 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 = Just <$> calcRepo (gitAnnexContentLock key)
+contentLockFile key _ = Just <$> calcRepo (gitAnnexContentLock key)
#endif
{- Performs an action, passing it the location to use for a key's content. -}
gitAnnexLink,
gitAnnexLinkCanonical,
gitAnnexContentLock,
+ gitAnnexContentLockLock,
gitAnnexInodeSentinal,
gitAnnexInodeSentinalCache,
annexLocationsBare,
loc <- gitAnnexLocation key r config
return $ loc <> ".lck"
+{- Lock that is held when taking the gitAnnexContentLock to support the v10
+ - upgrade. -}
+gitAnnexContentLockLock :: Git.Repo -> RawFilePath
+gitAnnexContentLockLock r = gitAnnexDir r P.</> "content.lck"
+
gitAnnexInodeSentinal :: Git.Repo -> RawFilePath
gitAnnexInodeSentinal r = gitAnnexDir r P.</> "sentinal"
import Annex.Common
import Types.Upgrade
-import Utility.Daemon
upgrade :: Bool -> Annex UpgradeResult
upgrade automatic = do
import Types.Upgrade
import Annex.Content
import Annex.Perms
+import Annex.LockFile
+import Annex.Version
import Git.ConfigTypes
import Types.RepoVersion
import Logs.Upgrade
upgrade :: Bool -> Annex UpgradeResult
upgrade automatic
| automatic = do
- -- For automatic upgrade, wait until a year after the v9
- -- upgrade. This is to give time for any old processes
- -- that were running before the v9 upgrade to finish.
- -- Such old processes lock content using the old method,
- -- and it is not safe for such to still be running after
- -- this upgrade.
+ {- For automatic upgrade, wait until a year after the v9
+ - upgrade. This is to give time for any old processes
+ - that were running before the v9 upgrade to finish.
+ - Such old processes lock content using the old method,
+ - and it is not safe for such to still be running after
+ - this upgrade. -}
timeOfUpgrade (RepoVersion 9) >>= \case
Nothing -> performUpgrade automatic
Just t -> do
performUpgrade automatic
| otherwise = performUpgrade automatic
where
- -- Skip upgrade when git-annex assistant (or watch) is running,
- -- because these are long-running daemons that could conceivably
- -- run for an entire year.
+ {- Skip upgrade when git-annex assistant (or watch) is running,
+ - because these are long-running daemons that could conceivably
+ - run for an entire year and so predate the v9 upgrade. -}
checkassistantrunning a = do
pidfile <- fromRepo gitAnnexPidFile
liftIO (checkDaemon (fromRawFilePath pidfile)) >>= \case
performUpgrade automatic = do
unless automatic $
showAction "v9 to v10"
+
+ {- Take a lock to ensure that there are no other git-annex
+ - processes running that are using the old content locking method. -}
+ withExclusiveLock gitAnnexContentLockLock $ do
+ {- When core.sharedRepository is set, object files
+ - used to have their write bits set. That can now be
+ - removed, if the user the upgrade is running as has
+ - permission to remove it.
+ - (Otherwise, a later fsck will fix up the permissions.) -}
+ withShared $ \sr -> case sr of
+ GroupShared -> removewrite sr
+ AllShared -> removewrite sr
+ _ -> return ()
- {- When core.sharedRepository is set, object files
- - used to have their write bits set. That can now be removed,
- - if the user the upgrade is running as has permission to remove
- - it. (Otherwise, a later fsck will fix up the permissions.) -}
- withShared $ \sr -> case sr of
- GroupShared -> removewrite sr
- AllShared -> removewrite sr
- _ -> return ()
+ {- Set the new version while still holding the lock,
+ - so that any other process waiting for the lock will
+ - be able to detect that the upgrade happened. -}
+ setVersion newver
- return UpgradeDeferred
+ return UpgradeSuccess
where
- newver = Just (RepoVersion 9)
+ newver = RepoVersion 10
removewrite sr = do
ks <- listKeys InAnnex
keystatus <- getKeyStatus k
case keystatus of
KeyPresent -> void $ tryIO $
- freezeContent'' sr obj newver
+ freezeContent'' sr obj (Just newver)
KeyUnlockedThin -> return ()
KeyLockedThin -> return ()
KeyMissing -> return ()
lock. But this seems to fail when a v9 process is running -- if it blocks
on the shared lock for the v10 upgrade, it would still go on the lock in v9
mode in the now v10 repository.
+
+Update: That problem can be avoided by re-reading the git config
+to check if v10 was enabled, once it has taken the shared lock.
+That will mean that v9 repos do a little bit more work when locking content
+and dropping. For efficiency, use an InodeCache and only re-read when it's changed.
+This will need the v10 upgrade to set annex.version while it is still
+holding the exclusive lock.
"""]]