2012-04-21 20:59:49 +00:00
|
|
|
{- git-annex file permissions
|
|
|
|
-
|
2020-03-05 18:27:45 +00:00
|
|
|
- Copyright 2012-2020 Joey Hess <id@joeyh.name>
|
2012-04-21 20:59:49 +00:00
|
|
|
-
|
2019-03-13 19:48:14 +00:00
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
2012-04-21 20:59:49 +00:00
|
|
|
-}
|
|
|
|
|
|
|
|
module Annex.Perms (
|
2018-03-15 17:20:38 +00:00
|
|
|
FileMode,
|
2013-11-18 22:05:37 +00:00
|
|
|
setAnnexFilePerm,
|
|
|
|
setAnnexDirPerm,
|
2020-09-02 18:25:12 +00:00
|
|
|
resetAnnexFilePerm,
|
2012-04-21 20:59:49 +00:00
|
|
|
annexFileMode,
|
|
|
|
createAnnexDirectory,
|
2020-03-06 15:40:20 +00:00
|
|
|
createWorkTreeDirectory,
|
2012-04-21 20:59:49 +00:00
|
|
|
noUmask,
|
2016-03-09 17:43:22 +00:00
|
|
|
freezeContent,
|
2016-04-14 19:36:53 +00:00
|
|
|
isContentWritePermOk,
|
2016-03-09 17:43:22 +00:00
|
|
|
thawContent,
|
2013-01-26 09:09:15 +00:00
|
|
|
createContentDir,
|
|
|
|
freezeContentDir,
|
2013-04-30 23:09:36 +00:00
|
|
|
thawContentDir,
|
2013-11-15 18:52:03 +00:00
|
|
|
modifyContent,
|
2015-05-19 19:04:24 +00:00
|
|
|
withShared,
|
2012-04-21 20:59:49 +00:00
|
|
|
) where
|
|
|
|
|
2016-01-20 20:36:33 +00:00
|
|
|
import Annex.Common
|
2012-04-21 20:59:49 +00:00
|
|
|
import Utility.FileMode
|
2020-03-06 15:40:20 +00:00
|
|
|
import Git
|
2017-02-17 18:04:43 +00:00
|
|
|
import Git.ConfigTypes
|
2012-04-21 23:42:49 +00:00
|
|
|
import qualified Annex
|
2013-02-14 18:10:36 +00:00
|
|
|
import Config
|
2020-10-28 21:25:59 +00:00
|
|
|
import Utility.Directory.Create
|
|
|
|
import qualified Utility.RawFilePath as R
|
2012-04-21 20:59:49 +00:00
|
|
|
|
2012-04-21 23:42:49 +00:00
|
|
|
withShared :: (SharedRepository -> Annex a) -> Annex a
|
2015-05-19 19:04:24 +00:00
|
|
|
withShared a = a =<< coreSharedRepository <$> Annex.getGitConfig
|
2012-04-21 23:42:49 +00:00
|
|
|
|
2020-11-05 22:45:37 +00:00
|
|
|
setAnnexFilePerm :: RawFilePath -> Annex ()
|
2013-11-18 22:05:37 +00:00
|
|
|
setAnnexFilePerm = setAnnexPerm False
|
|
|
|
|
2020-11-05 22:45:37 +00:00
|
|
|
setAnnexDirPerm :: RawFilePath -> Annex ()
|
2013-11-18 22:05:37 +00:00
|
|
|
setAnnexDirPerm = setAnnexPerm True
|
|
|
|
|
2012-04-21 20:59:49 +00:00
|
|
|
{- Sets appropriate file mode for a file or directory in the annex,
|
|
|
|
- other than the content files and content directory. Normally,
|
2020-09-02 18:25:12 +00:00
|
|
|
- don't change the mode, but with core.sharedRepository set,
|
2012-04-21 20:59:49 +00:00
|
|
|
- allow the group to write, etc. -}
|
2020-11-05 22:45:37 +00:00
|
|
|
setAnnexPerm :: Bool -> RawFilePath -> Annex ()
|
2020-09-02 18:25:12 +00:00
|
|
|
setAnnexPerm = setAnnexPerm' Nothing
|
|
|
|
|
2020-11-05 22:45:37 +00:00
|
|
|
setAnnexPerm' :: Maybe ([FileMode] -> FileMode -> FileMode) -> Bool -> RawFilePath -> Annex ()
|
2020-09-02 18:25:12 +00:00
|
|
|
setAnnexPerm' modef isdir file = unlessM crippledFileSystem $
|
2013-02-14 18:10:36 +00:00
|
|
|
withShared $ liftIO . go
|
2012-12-13 04:24:19 +00:00
|
|
|
where
|
2020-09-02 18:25:12 +00:00
|
|
|
go GroupShared = void $ tryIO $ modifyFileMode file $ modef' $
|
2013-11-18 22:05:37 +00:00
|
|
|
groupSharedModes ++
|
|
|
|
if isdir then [ ownerExecuteMode, groupExecuteMode ] else []
|
2020-09-02 18:25:12 +00:00
|
|
|
go AllShared = void $ tryIO $ modifyFileMode file $ modef' $
|
2013-11-18 22:05:37 +00:00
|
|
|
readModes ++
|
|
|
|
[ ownerWriteMode, groupWriteMode ] ++
|
|
|
|
if isdir then executeModes else []
|
2020-09-02 18:25:12 +00:00
|
|
|
go _ = case modef of
|
|
|
|
Nothing -> noop
|
|
|
|
Just f -> void $ tryIO $
|
|
|
|
modifyFileMode file $ f []
|
|
|
|
modef' = fromMaybe addModes modef
|
|
|
|
|
2020-11-05 22:45:37 +00:00
|
|
|
resetAnnexFilePerm :: RawFilePath -> Annex ()
|
2020-09-02 18:25:12 +00:00
|
|
|
resetAnnexFilePerm = resetAnnexPerm False
|
|
|
|
|
|
|
|
{- Like setAnnexPerm, but ignores the current mode of the file entirely,
|
|
|
|
- and sets the same mode that the umask would result in when creating a
|
|
|
|
- new file.
|
|
|
|
-
|
|
|
|
- Useful eg, after creating a temporary file with locked down modes,
|
|
|
|
- which is going to be moved to a non-temporary location and needs
|
|
|
|
- usual modes.
|
|
|
|
-}
|
2020-11-05 22:45:37 +00:00
|
|
|
resetAnnexPerm :: Bool -> RawFilePath -> Annex ()
|
2020-09-02 18:25:12 +00:00
|
|
|
resetAnnexPerm isdir file = unlessM crippledFileSystem $ do
|
|
|
|
defmode <- liftIO defaultFileMode
|
|
|
|
let modef moremodes _oldmode = addModes moremodes defmode
|
|
|
|
setAnnexPerm' (Just modef) isdir file
|
2012-04-21 20:59:49 +00:00
|
|
|
|
|
|
|
{- Gets the appropriate mode to use for creating a file in the annex
|
2020-09-02 18:25:12 +00:00
|
|
|
- (other than content files, which are locked down more). The umask is not
|
|
|
|
- taken into account; this is for use with actions that create the file
|
|
|
|
- and apply the umask automatically. -}
|
2012-04-21 20:59:49 +00:00
|
|
|
annexFileMode :: Annex FileMode
|
2012-04-21 23:42:49 +00:00
|
|
|
annexFileMode = withShared $ return . go
|
2012-12-13 04:24:19 +00:00
|
|
|
where
|
|
|
|
go GroupShared = sharedmode
|
|
|
|
go AllShared = combineModes (sharedmode:readModes)
|
|
|
|
go _ = stdFileMode
|
2013-11-18 22:05:37 +00:00
|
|
|
sharedmode = combineModes groupSharedModes
|
2012-04-21 20:59:49 +00:00
|
|
|
|
2020-03-05 18:27:45 +00:00
|
|
|
{- Creates a directory inside the gitAnnexDir, creating any parent
|
|
|
|
- directories up to and including the gitAnnexDir.
|
|
|
|
- Makes directories with appropriate permissions. -}
|
2020-10-28 21:25:59 +00:00
|
|
|
createAnnexDirectory :: RawFilePath -> Annex ()
|
2020-03-05 18:27:45 +00:00
|
|
|
createAnnexDirectory dir = do
|
2020-10-28 21:25:59 +00:00
|
|
|
top <- parentDir <$> fromRepo gitAnnexDir
|
2020-03-05 18:27:45 +00:00
|
|
|
createDirectoryUnder' top dir createdir
|
2012-12-13 04:24:19 +00:00
|
|
|
where
|
2020-03-05 18:27:45 +00:00
|
|
|
createdir p = do
|
2020-10-28 21:25:59 +00:00
|
|
|
liftIO $ R.createDirectory p
|
2020-11-05 22:45:37 +00:00
|
|
|
setAnnexDirPerm p
|
2013-01-26 09:09:15 +00:00
|
|
|
|
2020-03-06 15:40:20 +00:00
|
|
|
{- Create a directory in the git work tree, creating any parent
|
|
|
|
- directories up to the top of the work tree.
|
|
|
|
-
|
|
|
|
- Uses default permissions.
|
|
|
|
-}
|
2020-10-28 21:25:59 +00:00
|
|
|
createWorkTreeDirectory :: RawFilePath -> Annex ()
|
2020-03-06 15:40:20 +00:00
|
|
|
createWorkTreeDirectory dir = do
|
|
|
|
fromRepo repoWorkTree >>= liftIO . \case
|
2020-10-28 21:25:59 +00:00
|
|
|
Just wt -> createDirectoryUnder wt dir
|
2020-03-06 15:40:20 +00:00
|
|
|
-- Should never happen, but let whatever tries to write
|
|
|
|
-- to the directory be what throws an exception, as that
|
|
|
|
-- will be clearer than an exception from here.
|
|
|
|
Nothing -> noop
|
|
|
|
|
2016-03-09 17:43:22 +00:00
|
|
|
{- Normally, blocks writing to an annexed file, and modifies file
|
|
|
|
- permissions to allow reading it.
|
|
|
|
-
|
|
|
|
- When core.sharedRepository is set, the write bits are not removed from
|
|
|
|
- the file, but instead the appropriate group write bits are set. This is
|
2016-04-14 19:36:53 +00:00
|
|
|
- necessary to let other users in the group lock the file. But, in a
|
|
|
|
- shared repository, the current user may not be able to change a file
|
|
|
|
- owned by another user, so failure to set this mode is ignored.
|
2016-03-09 17:43:22 +00:00
|
|
|
-}
|
2020-11-05 22:45:37 +00:00
|
|
|
freezeContent :: RawFilePath -> Annex ()
|
2016-03-09 17:43:22 +00:00
|
|
|
freezeContent file = unlessM crippledFileSystem $
|
|
|
|
withShared go
|
|
|
|
where
|
2016-04-14 19:36:53 +00:00
|
|
|
go GroupShared = liftIO $ void $ tryIO $ modifyFileMode file $
|
2016-03-09 17:43:22 +00:00
|
|
|
addModes [ownerReadMode, groupReadMode, ownerWriteMode, groupWriteMode]
|
2016-04-14 19:36:53 +00:00
|
|
|
go AllShared = liftIO $ void $ tryIO $ modifyFileMode file $
|
2016-03-09 17:43:22 +00:00
|
|
|
addModes (readModes ++ writeModes)
|
|
|
|
go _ = liftIO $ modifyFileMode file $
|
|
|
|
removeModes writeModes .
|
|
|
|
addModes [ownerReadMode]
|
|
|
|
|
2020-11-03 14:11:04 +00:00
|
|
|
isContentWritePermOk :: RawFilePath -> Annex Bool
|
2016-04-14 19:36:53 +00:00
|
|
|
isContentWritePermOk file = ifM crippledFileSystem
|
|
|
|
( return True
|
|
|
|
, withShared go
|
|
|
|
)
|
|
|
|
where
|
|
|
|
go GroupShared = want [ownerWriteMode, groupWriteMode]
|
|
|
|
go AllShared = want writeModes
|
|
|
|
go _ = return True
|
2017-12-05 19:00:50 +00:00
|
|
|
want wantmode =
|
2020-11-03 14:11:04 +00:00
|
|
|
liftIO (catchMaybeIO $ fileMode <$> R.getFileStatus file) >>= return . \case
|
2016-04-14 19:36:53 +00:00
|
|
|
Nothing -> True
|
|
|
|
Just havemode -> havemode == combineModes (havemode:wantmode)
|
|
|
|
|
2016-03-09 17:43:22 +00:00
|
|
|
{- Allows writing to an annexed file that freezeContent was called on
|
|
|
|
- before. -}
|
2020-11-05 22:45:37 +00:00
|
|
|
thawContent :: RawFilePath -> Annex ()
|
2016-03-09 17:43:22 +00:00
|
|
|
thawContent file = thawPerms $ withShared go
|
|
|
|
where
|
2016-04-14 19:36:53 +00:00
|
|
|
go GroupShared = liftIO $ void $ tryIO $ groupWriteRead file
|
|
|
|
go AllShared = liftIO $ void $ tryIO $ groupWriteRead file
|
2016-03-09 17:43:22 +00:00
|
|
|
go _ = liftIO $ allowWrite file
|
|
|
|
|
|
|
|
{- Runs an action that thaws a file's permissions. This will probably
|
|
|
|
- fail on a crippled filesystem. But, if file modes are supported on a
|
|
|
|
- crippled filesystem, the file may be frozen, so try to thaw it. -}
|
|
|
|
thawPerms :: Annex () -> Annex ()
|
|
|
|
thawPerms a = ifM crippledFileSystem
|
|
|
|
( void $ tryNonAsync a
|
|
|
|
, a
|
|
|
|
)
|
|
|
|
|
2013-01-26 09:09:15 +00:00
|
|
|
{- Blocks writing to the directory an annexed file is in, to prevent the
|
2017-02-11 09:38:49 +00:00
|
|
|
- file accidentally being deleted. However, if core.sharedRepository
|
2013-01-26 09:09:15 +00:00
|
|
|
- is set, this is not done, since the group must be allowed to delete the
|
|
|
|
- file.
|
|
|
|
-}
|
2020-10-28 21:25:59 +00:00
|
|
|
freezeContentDir :: RawFilePath -> Annex ()
|
2013-02-14 18:10:36 +00:00
|
|
|
freezeContentDir file = unlessM crippledFileSystem $
|
2015-05-19 19:04:24 +00:00
|
|
|
withShared go
|
2013-01-26 09:09:15 +00:00
|
|
|
where
|
2020-11-05 22:45:37 +00:00
|
|
|
dir = parentDir file
|
2016-04-14 19:36:53 +00:00
|
|
|
go GroupShared = liftIO $ void $ tryIO $ groupWriteRead dir
|
|
|
|
go AllShared = liftIO $ void $ tryIO $ groupWriteRead dir
|
2015-05-19 19:04:24 +00:00
|
|
|
go _ = liftIO $ preventWrite dir
|
2013-01-26 09:09:15 +00:00
|
|
|
|
2020-10-28 21:25:59 +00:00
|
|
|
thawContentDir :: RawFilePath -> Annex ()
|
|
|
|
thawContentDir file =
|
2020-11-05 22:45:37 +00:00
|
|
|
thawPerms $ liftIO $ allowWrite $ parentDir file
|
2013-04-30 23:09:36 +00:00
|
|
|
|
2013-01-26 09:09:15 +00:00
|
|
|
{- Makes the directory tree to store an annexed file's content,
|
|
|
|
- with appropriate permissions on each level. -}
|
2020-10-28 21:25:59 +00:00
|
|
|
createContentDir :: RawFilePath -> Annex ()
|
2013-01-26 09:09:15 +00:00
|
|
|
createContentDir dest = do
|
2020-10-28 21:25:59 +00:00
|
|
|
unlessM (liftIO $ R.doesPathExist dir) $
|
2013-01-26 09:09:15 +00:00
|
|
|
createAnnexDirectory dir
|
|
|
|
-- might have already existed with restricted perms
|
2013-02-14 18:10:36 +00:00
|
|
|
unlessM crippledFileSystem $
|
2020-11-05 22:45:37 +00:00
|
|
|
liftIO $ allowWrite dir
|
2013-01-26 09:09:15 +00:00
|
|
|
where
|
2015-01-09 17:11:56 +00:00
|
|
|
dir = parentDir dest
|
2013-11-15 18:52:03 +00:00
|
|
|
|
|
|
|
{- Creates the content directory for a file if it doesn't already exist,
|
|
|
|
- or thaws it if it does, then runs an action to modify the file, and
|
|
|
|
- finally, freezes the content directory. -}
|
2020-10-28 21:25:59 +00:00
|
|
|
modifyContent :: RawFilePath -> Annex a -> Annex a
|
2013-11-15 18:52:03 +00:00
|
|
|
modifyContent f a = do
|
|
|
|
createContentDir f -- also thaws it
|
unify exception handling into Utility.Exception
Removed old extensible-exceptions, only needed for very old ghc.
Made webdav use Utility.Exception, to work after some changes in DAV's
exception handling.
Removed Annex.Exception. Mostly this was trivial, but note that
tryAnnex is replaced with tryNonAsync and catchAnnex replaced with
catchNonAsync. In theory that could be a behavior change, since the former
caught all exceptions, and the latter don't catch async exceptions.
However, in practice, nothing in the Annex monad uses async exceptions.
Grepping for throwTo and killThread only find stuff in the assistant,
which does not seem related.
Command.Add.undo is changed to accept a SomeException, and things
that use it for rollback now catch non-async exceptions, rather than
only IOExceptions.
2014-08-08 01:55:44 +00:00
|
|
|
v <- tryNonAsync a
|
2013-11-15 18:52:03 +00:00
|
|
|
freezeContentDir f
|
unify exception handling into Utility.Exception
Removed old extensible-exceptions, only needed for very old ghc.
Made webdav use Utility.Exception, to work after some changes in DAV's
exception handling.
Removed Annex.Exception. Mostly this was trivial, but note that
tryAnnex is replaced with tryNonAsync and catchAnnex replaced with
catchNonAsync. In theory that could be a behavior change, since the former
caught all exceptions, and the latter don't catch async exceptions.
However, in practice, nothing in the Annex monad uses async exceptions.
Grepping for throwTo and killThread only find stuff in the assistant,
which does not seem related.
Command.Add.undo is changed to accept a SomeException, and things
that use it for rollback now catch non-async exceptions, rather than
only IOExceptions.
2014-08-08 01:55:44 +00:00
|
|
|
either throwM return v
|