Support git's core.sharedRepository configuration

This is incomplete, it does not honor it yet for hash directories
and other annex bookkeeping files. Some of that is not needed for a bare
repo; some of it may be.
This commit is contained in:
Joey Hess 2012-04-21 14:06:36 -04:00
parent 10d3e91626
commit b4a5e39ee6
7 changed files with 155 additions and 52 deletions

View file

@ -23,10 +23,11 @@ module Annex.Content (
saveState, saveState,
downloadUrl, downloadUrl,
preseedTmp, preseedTmp,
freezeContent,
thawContent,
freezeContentDir,
) where ) where
import Control.Exception (bracket_)
import System.Posix.Types
import System.IO.Unsafe (unsafeInterleaveIO) import System.IO.Unsafe (unsafeInterleaveIO)
import Common.Annex import Common.Annex
@ -44,6 +45,7 @@ import Utility.DataUnits
import Utility.CopyFile import Utility.CopyFile
import Config import Config
import Annex.Exception import Annex.Exception
import Git.SharedRepository
{- Checks if a given key's content is currently present. -} {- Checks if a given key's content is currently present. -}
inAnnex :: Key -> Annex Bool inAnnex :: Key -> Annex Bool
@ -57,8 +59,10 @@ inAnnex' a key = do
{- A safer check; the key's content must not only be present, but {- A safer check; the key's content must not only be present, but
- is not in the process of being removed. -} - is not in the process of being removed. -}
inAnnexSafe :: Key -> Annex (Maybe Bool) inAnnexSafe :: Key -> Annex (Maybe Bool)
inAnnexSafe = inAnnex' $ \f -> openForLock f False >>= check inAnnexSafe = inAnnex' $ \f -> openforlock f >>= check
where where
openforlock f = catchMaybeIO $
openFd f ReadOnly Nothing defaultFileFlags
check Nothing = return is_missing check Nothing = return is_missing
check (Just h) = do check (Just h) = do
v <- getLock h (ReadLock, AbsoluteSeek, 0, 0) v <- getLock h (ReadLock, AbsoluteSeek, 0, 0)
@ -75,30 +79,27 @@ inAnnexSafe = inAnnex' $ \f -> openForLock f False >>= check
lockContent :: Key -> Annex a -> Annex a lockContent :: Key -> Annex a -> Annex a
lockContent key a = do lockContent key a = do
file <- inRepo $ gitAnnexLocation key file <- inRepo $ gitAnnexLocation key
bracketIO (openForLock file True >>= lock) unlock a bracketIO (openforlock file >>= lock) unlock a
where where
{- Since 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)
( withModifiedFileMode f
(\cur -> cur `unionFileModes` ownerWriteMode)
open
, open
)
where
open = openFd f ReadWrite Nothing defaultFileFlags
lock Nothing = return Nothing lock Nothing = return Nothing
lock (Just l) = do lock (Just fd) = do
v <- tryIO $ setLock l (WriteLock, AbsoluteSeek, 0, 0) v <- tryIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
case v of case v of
Left _ -> error "content is locked" Left _ -> error "content is locked"
Right _ -> return $ Just l Right _ -> return $ Just fd
unlock Nothing = return () unlock Nothing = return ()
unlock (Just l) = closeFd l unlock (Just l) = closeFd l
openForLock :: FilePath -> Bool -> IO (Maybe Fd)
openForLock file writelock = bracket_ prep cleanup go
where
go = catchMaybeIO $ openFd file mode Nothing defaultFileFlags
mode = if writelock then ReadWrite else ReadOnly
{- Since files are stored with the write bit disabled,
- have to fiddle with permissions to open for an
- exclusive lock. -}
forwritelock a =
when writelock $ whenM (doesFileExist file) a
prep = forwritelock $ allowWrite file
cleanup = forwritelock $ preventWrite file
{- Calculates the relative path to use to link a file to a key. -} {- Calculates the relative path to use to link a file to a key. -}
calcGitLink :: FilePath -> Key -> Annex FilePath calcGitLink :: FilePath -> Key -> Annex FilePath
calcGitLink file key = do calcGitLink file key = do
@ -132,7 +133,7 @@ getViaTmp key action = do
else return 0 else return 0
ifM (checkDiskSpace Nothing key alreadythere) ifM (checkDiskSpace Nothing key alreadythere)
( do ( do
when e $ liftIO $ allowWrite tmp when e $ thawContent tmp
getViaTmpUnchecked key action getViaTmpUnchecked key action
, return False , return False
) )
@ -216,14 +217,15 @@ moveAnnex :: Key -> FilePath -> Annex ()
moveAnnex key src = do moveAnnex key src = do
dest <- inRepo $ gitAnnexLocation key dest <- inRepo $ gitAnnexLocation key
let dir = parentDir dest let dir = parentDir dest
liftIO $ ifM (doesFileExist dest) ifM (liftIO $ doesFileExist dest)
( removeFile src ( liftIO $ removeFile src
, do , do
createDirectoryIfMissing True dir liftIO $ do
allowWrite dir -- in case the directory already exists createDirectoryIfMissing True dir
moveFile src dest allowWrite dir -- in case the directory already exists
preventWrite dest moveFile src dest
preventWrite dir freezeContent dest
freezeContentDir dest
) )
withObjectLoc :: Key -> ((FilePath, FilePath) -> Annex a) -> Annex a withObjectLoc :: Key -> ((FilePath, FilePath) -> Annex a) -> Annex a
@ -254,10 +256,9 @@ removeAnnex key = withObjectLoc key $ \(dir, file) -> do
{- Moves a key's file out of .git/annex/objects/ -} {- Moves a key's file out of .git/annex/objects/ -}
fromAnnex :: Key -> FilePath -> Annex () fromAnnex :: Key -> FilePath -> Annex ()
fromAnnex key dest = withObjectLoc key $ \(dir, file) -> do fromAnnex key dest = withObjectLoc key $ \(dir, file) -> do
liftIO $ do liftIO $ allowWrite dir
allowWrite dir thawContent file
allowWrite file liftIO $ moveFile file dest
moveFile file dest
cleanObjectLoc key cleanObjectLoc key
{- Moves a key out of .git/annex/objects/ into .git/annex/bad, and {- Moves a key out of .git/annex/objects/ into .git/annex/bad, and
@ -321,7 +322,7 @@ preseedTmp key file = go =<< inAnnex key
go False = return False go False = return False
go True = do go True = do
ok <- copy ok <- copy
when ok $ liftIO $ allowWrite file when ok $ thawContent file
return ok return ok
copy = ifM (liftIO $ doesFileExist file) copy = ifM (liftIO $ doesFileExist file)
( return True ( return True
@ -329,3 +330,40 @@ preseedTmp key file = go =<< inAnnex key
s <- inRepo $ gitAnnexLocation key s <- inRepo $ gitAnnexLocation key
liftIO $ copyFileExternal s file liftIO $ copyFileExternal s file
) )
{- Blocks writing to an annexed file. The file is made unwritable
- to avoid accidental edits. core.sharedRepository may change
- who can read it. -}
freezeContent :: FilePath -> Annex ()
freezeContent file = liftIO . go =<< fromRepo getSharedRepository
where
go GroupShared = do
preventWrite file
groupRead file
go AllShared = do
preventWrite file
allRead file
go _ = preventWrite file
{- Allows writing to an annexed file that freezeContent was called on
- before. -}
thawContent :: FilePath -> Annex ()
thawContent file = liftIO . go =<< fromRepo getSharedRepository
where
go GroupShared = groupWriteRead file
go AllShared = groupWriteRead file
go _ = allowWrite file
{- Blocks writing to the directory an annexed file is in, to prevent the
- file accidentially being deleted. However, if core.sharedRepository
- is set, this is not done, since the group must be allowed to delete the
- file.
-}
freezeContentDir :: FilePath -> Annex ()
freezeContentDir file = liftIO . go =<< fromRepo getSharedRepository
where
dir = parentDir file
go GroupShared = groupWriteRead dir
go AllShared = groupWriteRead dir
go _ = preventWrite dir

View file

@ -166,10 +166,9 @@ verifyLocationLog key desc = do
-- Since we're checking that a key's file is present, throw -- Since we're checking that a key's file is present, throw
-- in a permission fixup here too. -- in a permission fixup here too.
when present $ do when present $ do
f <- inRepo $ gitAnnexLocation key file <- inRepo $ gitAnnexLocation key
liftIO $ do freezeContent file
preventWrite f freezeContentDir file
preventWrite (parentDir f)
u <- getUUID u <- getUUID
verifyLocationLog' key desc present u (logChange key u) verifyLocationLog' key desc present u (logChange key u)

View file

@ -10,7 +10,6 @@ module Command.Unannex where
import Common.Annex import Common.Annex
import Command import Command
import qualified Annex import qualified Annex
import Utility.FileMode
import Logs.Location import Logs.Location
import Annex.Content import Annex.Content
import qualified Git.Command import qualified Git.Command
@ -51,9 +50,8 @@ cleanup file key = do
( do ( do
-- fast mode: hard link to content in annex -- fast mode: hard link to content in annex
src <- inRepo $ gitAnnexLocation key src <- inRepo $ gitAnnexLocation key
liftIO $ do liftIO $ createLink src file
createLink src file thawContent file
allowWrite file
, do , do
fromAnnex key file fromAnnex key file
logStatus key InfoMissing logStatus key InfoMissing

View file

@ -46,6 +46,6 @@ perform dest key = do
liftIO $ do liftIO $ do
removeFile dest removeFile dest
moveFile tmpdest dest moveFile tmpdest dest
allowWrite dest thawContent dest
next $ return True next $ return True
else error "copy failed!" else error "copy failed!"

27
Git/SharedRepository.hs Normal file
View file

@ -0,0 +1,27 @@
{- git core.sharedRepository handling
-
- Copyright 2012 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Git.SharedRepository where
import Data.Char
import Common
import Git
import qualified Git.Config
data SharedRepository = UnShared | GroupShared | AllShared | UmaskShared Int
getSharedRepository :: Repo -> SharedRepository
getSharedRepository r =
case map toLower $ Git.Config.get "core.sharedrepository" "" r of
"1" -> GroupShared
"group" -> GroupShared
"true" -> GroupShared
"all" -> AllShared
"world" -> AllShared
"everybody" -> AllShared
v -> maybe UnShared UmaskShared (readish v)

View file

@ -1,6 +1,6 @@
{- File mode utilities. {- File mode utilities.
- -
- Copyright 2010 Joey Hess <joey@kitenet.net> - Copyright 2010-2012 Joey Hess <joey@kitenet.net>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -9,16 +9,36 @@ module Utility.FileMode where
import Common import Common
import Control.Exception (bracket)
import System.Posix.Types import System.Posix.Types
import Foreign (complement) import Foreign (complement)
combineModes :: [FileMode] -> FileMode
combineModes [] = undefined
combineModes [m] = m
combineModes (m:ms) = foldl unionFileModes m ms
{- Applies a conversion function to a file's mode. -}
modifyFileMode :: FilePath -> (FileMode -> FileMode) -> IO () modifyFileMode :: FilePath -> (FileMode -> FileMode) -> IO ()
modifyFileMode f convert = do modifyFileMode f convert = do
_ <- modifyFileMode' f convert
return ()
modifyFileMode' :: FilePath -> (FileMode -> FileMode) -> IO FileMode
modifyFileMode' f convert = do
s <- getFileStatus f s <- getFileStatus f
let cur = fileMode s let old = fileMode s
let new = convert cur let new = convert old
when (new /= cur) $ when (new /= old) $
setFileMode f new setFileMode f new
return old
{- Runs an action after changing a file's mode, then restores the old mode. -}
withModifiedFileMode :: FilePath -> (FileMode -> FileMode) -> IO a -> IO a
withModifiedFileMode file convert a = bracket setup cleanup go
where
setup = modifyFileMode' file convert
cleanup oldmode = modifyFileMode file (const oldmode)
go _ = a
{- Removes a FileMode from a file. {- Removes a FileMode from a file.
- For example, call with otherWriteMode to chmod o-w -} - For example, call with otherWriteMode to chmod o-w -}
@ -28,23 +48,43 @@ unsetFileMode f m = modifyFileMode f $
{- Removes the write bits from a file. -} {- Removes the write bits from a file. -}
preventWrite :: FilePath -> IO () preventWrite :: FilePath -> IO ()
preventWrite f = unsetFileMode f writebits preventWrite f = unsetFileMode f $ combineModes writebits
where where
writebits = foldl unionFileModes ownerWriteMode writebits = [ownerWriteMode, groupWriteMode, otherWriteMode]
[groupWriteMode, otherWriteMode]
{- Turns a file's write bit back on. -} {- Turns a file's write bit back on. -}
allowWrite :: FilePath -> IO () allowWrite :: FilePath -> IO ()
allowWrite f = modifyFileMode f $ allowWrite f = modifyFileMode f $
\cur -> cur `unionFileModes` ownerWriteMode \cur -> cur `unionFileModes` ownerWriteMode
{- Allows owner and group to read and write to a file. -}
groupWriteRead :: FilePath -> IO ()
groupWriteRead f = modifyFileMode f $ \cur -> combineModes
[ cur
, ownerWriteMode, groupWriteMode
, ownerReadMode, groupReadMode
]
{- Allows group to read a file. -}
groupRead :: FilePath -> IO ()
groupRead f = modifyFileMode f $ \cur -> combineModes
[ cur
, ownerReadMode, groupReadMode
]
{- Allows all to read a file. -}
allRead :: FilePath -> IO ()
allRead f = modifyFileMode f $ \cur -> combineModes
[ cur
, ownerReadMode, groupReadMode, otherReadMode
]
{- Checks if a file mode indicates it's a symlink. -} {- Checks if a file mode indicates it's a symlink. -}
isSymLink :: FileMode -> Bool isSymLink :: FileMode -> Bool
isSymLink mode = symbolicLinkMode `intersectFileModes` mode == symbolicLinkMode isSymLink mode = symbolicLinkMode `intersectFileModes` mode == symbolicLinkMode
{- Checks if a file has any executable bits set. -} {- Checks if a file has any executable bits set. -}
isExecutable :: FileMode -> Bool isExecutable :: FileMode -> Bool
isExecutable mode = ebits `intersectFileModes` mode /= 0 isExecutable mode = combineModes ebits `intersectFileModes` mode /= 0
where where
ebits = ownerExecuteMode `unionFileModes` ebits = [ownerExecuteMode, groupExecuteMode, otherExecuteMode]
groupExecuteMode `unionFileModes` otherExecuteMode

1
debian/changelog vendored
View file

@ -2,6 +2,7 @@ git-annex (3.20120419) UNRELEASED; urgency=low
* Fix use of annex.diskreserve config setting. * Fix use of annex.diskreserve config setting.
* Directory special remotes now check annex.diskreserve. * Directory special remotes now check annex.diskreserve.
* Support git's core.sharedRepository configuration.
-- Joey Hess <joeyh@debian.org> Fri, 20 Apr 2012 16:14:08 -0400 -- Joey Hess <joeyh@debian.org> Fri, 20 Apr 2012 16:14:08 -0400