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:
parent
10d3e91626
commit
b4a5e39ee6
7 changed files with 155 additions and 52 deletions
104
Annex/Content.hs
104
Annex/Content.hs
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
27
Git/SharedRepository.hs
Normal 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)
|
|
@ -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
1
debian/changelog
vendored
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue