ignore write bits being set when there is a freeze hook

When annex.freezecontent-command is set, and the filesystem does not
support removing write bits, avoid treating it as a crippled filesystem.

The hook may be enough to prevent writing on its own, and some filesystems
ignore attempts to remove write bits.

Sponsored-by: Dartmouth College's Datalad project
This commit is contained in:
Joey Hess 2022-02-24 13:28:31 -04:00
parent 4144f73c18
commit 28bc5ce232
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
6 changed files with 46 additions and 18 deletions

View file

@ -251,6 +251,7 @@ probeCrippledFileSystem = withEventuallyCleanedOtherTmp $ \tmp -> do
(r, warnings) <- probeCrippledFileSystem' tmp (r, warnings) <- probeCrippledFileSystem' tmp
(Just (freezeContent' UnShared)) (Just (freezeContent' UnShared))
(Just (thawContent' UnShared)) (Just (thawContent' UnShared))
=<< hasFreezeHook
mapM_ warning warnings mapM_ warning warnings
return r return r
@ -259,11 +260,12 @@ probeCrippledFileSystem'
=> RawFilePath => RawFilePath
-> Maybe (RawFilePath -> m ()) -> Maybe (RawFilePath -> m ())
-> Maybe (RawFilePath -> m ()) -> Maybe (RawFilePath -> m ())
-> Bool
-> m (Bool, [String]) -> m (Bool, [String])
#ifdef mingw32_HOST_OS #ifdef mingw32_HOST_OS
probeCrippledFileSystem' _ _ _ = return (True, []) probeCrippledFileSystem' _ _ _ _ = return (True, [])
#else #else
probeCrippledFileSystem' tmp freezecontent thawcontent = do probeCrippledFileSystem' tmp freezecontent thawcontent hasfreezehook = do
let f = tmp P.</> "gaprobe" let f = tmp P.</> "gaprobe"
let f' = fromRawFilePath f let f' = fromRawFilePath f
liftIO $ writeFile f' "" liftIO $ writeFile f' ""
@ -282,7 +284,7 @@ probeCrippledFileSystem' tmp freezecontent thawcontent = do
-- running as root). But some crippled -- running as root). But some crippled
-- filesystems ignore write bit removals or ignore -- filesystems ignore write bit removals or ignore
-- permissions entirely. -- permissions entirely.
ifM ((== Just False) <$> liftIO (checkContentWritePerm' UnShared (toRawFilePath f) Nothing)) ifM ((== Just False) <$> liftIO (checkContentWritePerm' UnShared (toRawFilePath f) Nothing hasfreezehook))
( return (True, ["Filesystem does not allow removing write bit from files."]) ( return (True, ["Filesystem does not allow removing write bit from files."])
, liftIO $ ifM ((== 0) <$> getRealUserID) , liftIO $ ifM ((== 0) <$> getRealUserID)
( return (False, []) ( return (False, [])

View file

@ -26,6 +26,7 @@ module Annex.Perms (
thawContentDir, thawContentDir,
modifyContent, modifyContent,
withShared, withShared,
hasFreezeHook,
) where ) where
import Annex.Common import Annex.Common
@ -183,26 +184,35 @@ freezeContent'' sr file rv = do
- permissions of a file owned by another user. So if the permissions seem - permissions of a file owned by another user. So if the permissions seem
- wrong, but the repository is shared, returns Nothing. If the permissions - wrong, but the repository is shared, returns Nothing. If the permissions
- are wrong otherwise, returns Just False. - are wrong otherwise, returns Just False.
-
- When there is a freeze hook, it may prevent write in some way other than
- permissions. One use of a freeze hook is when the filesystem does not
- support removing write permissions, so when there is such a hook
- write permissions are ignored.
-} -}
checkContentWritePerm :: RawFilePath -> Annex (Maybe Bool) checkContentWritePerm :: RawFilePath -> Annex (Maybe Bool)
checkContentWritePerm file = ifM crippledFileSystem checkContentWritePerm file = ifM crippledFileSystem
( return (Just True) ( return (Just True)
, do , do
rv <- getVersion rv <- getVersion
withShared (\sr -> liftIO (checkContentWritePerm' sr file rv)) hasfreezehook <- hasFreezeHook
withShared $ \sr -> liftIO $
checkContentWritePerm' sr file rv hasfreezehook
) )
checkContentWritePerm' :: SharedRepository -> RawFilePath -> Maybe RepoVersion -> IO (Maybe Bool) checkContentWritePerm' :: SharedRepository -> RawFilePath -> Maybe RepoVersion -> Bool -> IO (Maybe Bool)
checkContentWritePerm' sr file rv = case sr of checkContentWritePerm' sr file rv hasfreezehook
GroupShared | hasfreezehook = return (Just True)
| versionNeedsWritableContentFiles rv -> want sharedret | otherwise = case sr of
(includemodes [ownerWriteMode, groupWriteMode]) GroupShared
| otherwise -> want sharedret (excludemodes writeModes) | versionNeedsWritableContentFiles rv -> want sharedret
AllShared (includemodes [ownerWriteMode, groupWriteMode])
| versionNeedsWritableContentFiles rv -> | otherwise -> want sharedret (excludemodes writeModes)
want sharedret (includemodes writeModes) AllShared
| otherwise -> want sharedret (excludemodes writeModes) | versionNeedsWritableContentFiles rv ->
_ -> want Just (excludemodes writeModes) want sharedret (includemodes writeModes)
| otherwise -> want sharedret (excludemodes writeModes)
_ -> want Just (excludemodes writeModes)
where where
want mk f = catchMaybeIO (fileMode <$> R.getFileStatus file) want mk f = catchMaybeIO (fileMode <$> R.getFileStatus file)
>>= return . \case >>= return . \case
@ -280,6 +290,9 @@ modifyContent f a = do
freezeContentDir f freezeContentDir f
either throwM return v either throwM return v
hasFreezeHook :: Annex Bool
hasFreezeHook = isJust . annexFreezeContentCommand <$> Annex.getGitConfig
freezeHook :: RawFilePath -> Annex () freezeHook :: RawFilePath -> Annex ()
freezeHook p = maybe noop go =<< annexFreezeContentCommand <$> Annex.getGitConfig freezeHook p = maybe noop go =<< annexFreezeContentCommand <$> Annex.getGitConfig
where where

View file

@ -6,6 +6,9 @@ git-annex (10.20220223) UNRELEASED; urgency=medium
be annexed like any other file. be annexed like any other file.
* smudge: Warn when encountering a pointer file that has other content * smudge: Warn when encountering a pointer file that has other content
appended to it. appended to it.
* When annex.freezecontent-command is set, and the filesystem does not
support removing write bits, avoid treating it as a crippled
filesystem.
-- Joey Hess <id@joeyh.name> Wed, 23 Feb 2022 14:14:09 -0400 -- Joey Hess <id@joeyh.name> Wed, 23 Feb 2022 14:14:09 -0400

View file

@ -145,7 +145,7 @@ runner opts
ensuretmpdir ensuretmpdir
crippledfilesystem <- fst <$> Annex.Init.probeCrippledFileSystem' crippledfilesystem <- fst <$> Annex.Init.probeCrippledFileSystem'
(toRawFilePath tmpdir) (toRawFilePath tmpdir)
Nothing Nothing Nothing Nothing False
adjustedbranchok <- Annex.AdjustedBranch.isGitVersionSupported adjustedbranchok <- Annex.AdjustedBranch.isGitVersionSupported
case tryIngredients ingredients (tastyOptionSet opts) (tests crippledfilesystem adjustedbranchok opts) of case tryIngredients ingredients (tastyOptionSet opts) (tests crippledfilesystem adjustedbranchok opts) of
Nothing -> error "No tests found!?" Nothing -> error "No tests found!?"

View file

@ -0,0 +1,8 @@
[[!comment format=mdwn
username="joey"
subject="""comment 5"""
date="2022-02-24T17:24:55Z"
content="""
Fixed it to ignore remaining write perms when annex.freezecontent-command
is set.
"""]]

View file

@ -1231,8 +1231,10 @@ repository, using [[git-annex-config]]. See its man page for a list.)
Usually the write permission bits are unset to protect annexed objects Usually the write permission bits are unset to protect annexed objects
from being modified or deleted. The freezecontent-command is run after from being modified or deleted. The freezecontent-command is run after
git-annex has removed the write bit. The thawcontent-command should undo git-annex has removed (or attempted to remove) the write bit, and can
its effect, and is run before git-annex restores the write bit. be used to prevent writing in some other way.
The thawcontent-command should undo its effect, and is run before
git-annex restores the write bit.
In the command line, %path is replaced with the file or directory to In the command line, %path is replaced with the file or directory to
operate on. operate on.