Direct mode .git/annex/objects directories are no longer left writable

Because that allowed writing to symlinks of files that are not present,
which followed the link and put bad content in an object location.

fsck: Fix up .git/annex/object directory permissions.

This commit was sponsored by an anonymous bitcoin donor.
This commit is contained in:
Joey Hess 2013-11-15 14:52:03 -04:00
parent b0f85b3e22
commit d48b00ebed
8 changed files with 56 additions and 41 deletions

View file

@ -29,7 +29,6 @@ module Annex.Content (
preseedTmp, preseedTmp,
freezeContent, freezeContent,
thawContent, thawContent,
cleanObjectLoc,
dirKeys, dirKeys,
) where ) where
@ -255,11 +254,9 @@ moveAnnex key src = withObjectLoc key storeobject storedirect
where where
storeobject dest = ifM (liftIO $ doesFileExist dest) storeobject dest = ifM (liftIO $ doesFileExist dest)
( alreadyhave ( alreadyhave
, do , modifyContent dest $ do
createContentDir dest
liftIO $ moveFile src dest liftIO $ moveFile src dest
freezeContent dest freezeContent dest
freezeContentDir dest
) )
storeindirect = storeobject =<< calcRepo (gitAnnexLocation key) storeindirect = storeobject =<< calcRepo (gitAnnexLocation key)
@ -273,7 +270,6 @@ moveAnnex key src = withObjectLoc key storeobject storedirect
storedirect = storedirect' storeindirect storedirect = storedirect' storeindirect
storedirect' fallback [] = fallback storedirect' fallback [] = fallback
storedirect' fallback (f:fs) = do storedirect' fallback (f:fs) = do
thawContentDir =<< calcRepo (gitAnnexLocation key)
thawContent src thawContent src
v <- isAnnexLink f v <- isAnnexLink f
if Just key == v if Just key == v
@ -349,11 +345,11 @@ withObjectLoc key indirect direct = ifM isDirect
where where
goindirect = indirect =<< calcRepo (gitAnnexLocation key) goindirect = indirect =<< calcRepo (gitAnnexLocation key)
cleanObjectLoc :: Key -> Annex () cleanObjectLoc :: Key -> Annex () -> Annex ()
cleanObjectLoc key = do cleanObjectLoc key cleaner = do
file <- calcRepo $ gitAnnexLocation key file <- calcRepo $ gitAnnexLocation key
unlessM crippledFileSystem $ void $ tryAnnexIO $ thawContentDir file
void $ liftIO $ catchMaybeIO $ allowWrite $ parentDir file cleaner
liftIO $ removeparents file (3 :: Int) liftIO $ removeparents file (3 :: Int)
where where
removeparents _ 0 = noop removeparents _ 0 = noop
@ -369,13 +365,10 @@ cleanObjectLoc key = do
removeAnnex :: Key -> Annex () removeAnnex :: Key -> Annex ()
removeAnnex key = withObjectLoc key remove removedirect removeAnnex key = withObjectLoc key remove removedirect
where where
remove file = do remove file = cleanObjectLoc key $ do
thawContentDir file
liftIO $ nukeFile file liftIO $ nukeFile file
removeInodeCache key removeInodeCache key
cleanObjectLoc key
removedirect fs = do removedirect fs = do
thawContentDir =<< calcRepo (gitAnnexLocation key)
cache <- recordedInodeCache key cache <- recordedInodeCache key
removeInodeCache key removeInodeCache key
mapM_ (resetfile cache) fs mapM_ (resetfile cache) fs
@ -389,12 +382,10 @@ removeAnnex key = withObjectLoc key remove removedirect
{- 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 = do fromAnnex key dest = cleanObjectLoc key $ do
file <- calcRepo $ gitAnnexLocation key file <- calcRepo $ gitAnnexLocation key
thawContentDir file
thawContent file thawContent file
liftIO $ moveFile file dest liftIO $ moveFile file dest
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
- returns the file it was moved to. -} - returns the file it was moved to. -}
@ -404,9 +395,8 @@ moveBad key = do
bad <- fromRepo gitAnnexBadDir bad <- fromRepo gitAnnexBadDir
let dest = bad </> takeFileName src let dest = bad </> takeFileName src
createAnnexDirectory (parentDir dest) createAnnexDirectory (parentDir dest)
thawContentDir src cleanObjectLoc key $
liftIO $ moveFile src dest liftIO $ moveFile src dest
cleanObjectLoc key
logStatus key InfoMissing logStatus key InfoMissing
return dest return dest

View file

@ -10,6 +10,7 @@ module Annex.Content.Direct (
associatedFilesRelative, associatedFilesRelative,
removeAssociatedFile, removeAssociatedFile,
removeAssociatedFileUnchecked, removeAssociatedFileUnchecked,
removeAssociatedFiles,
addAssociatedFile, addAssociatedFile,
goodContent, goodContent,
recordedInodeCache, recordedInodeCache,
@ -64,8 +65,8 @@ changeAssociatedFiles key transform = do
files <- associatedFilesRelative key files <- associatedFilesRelative key
let files' = transform files let files' = transform files
when (files /= files') $ do when (files /= files') $ do
createContentDir mapping modifyContent mapping $
liftIO $ viaTmp write mapping $ unlines files' liftIO $ viaTmp write mapping $ unlines files'
top <- fromRepo Git.repoPath top <- fromRepo Git.repoPath
return $ map (top </>) files' return $ map (top </>) files'
where where
@ -75,6 +76,13 @@ changeAssociatedFiles key transform = do
hPutStr h content hPutStr h content
hClose h hClose h
{- Removes the list of associated files. -}
removeAssociatedFiles :: Key -> Annex ()
removeAssociatedFiles key = do
mapping <- calcRepo $ gitAnnexMapping key
modifyContent mapping $
liftIO $ nukeFile mapping
{- Removes an associated file. Returns new associatedFiles value. {- Removes an associated file. Returns new associatedFiles value.
- Checks if this was the last copy of the object, and updates location - Checks if this was the last copy of the object, and updates location
- log. -} - log. -}
@ -142,16 +150,16 @@ addInodeCache key cache = do
{- Writes inode cache for a key. -} {- Writes inode cache for a key. -}
writeInodeCache :: Key -> [InodeCache] -> Annex () writeInodeCache :: Key -> [InodeCache] -> Annex ()
writeInodeCache key caches = withInodeCacheFile key $ \f -> do writeInodeCache key caches = withInodeCacheFile key $ \f ->
createContentDir f modifyContent f $
liftIO $ writeFile f $ liftIO $ writeFile f $
unlines $ map showInodeCache caches unlines $ map showInodeCache caches
{- Removes an inode cache. -} {- Removes an inode cache. -}
removeInodeCache :: Key -> Annex () removeInodeCache :: Key -> Annex ()
removeInodeCache key = withInodeCacheFile key $ \f -> do removeInodeCache key = withInodeCacheFile key $ \f ->
createContentDir f -- also thaws directory modifyContent f $
liftIO $ nukeFile f liftIO $ nukeFile f
withInodeCacheFile :: Key -> (FilePath -> Annex a) -> Annex a withInodeCacheFile :: Key -> (FilePath -> Annex a) -> Annex a
withInodeCacheFile key a = a =<< calcRepo (gitAnnexInodeCache key) withInodeCacheFile key a = a =<< calcRepo (gitAnnexInodeCache key)

View file

@ -210,11 +210,11 @@ toDirectGen k f = do
where where
fromindirect loc = do fromindirect loc = do
{- Move content from annex to direct file. -} {- Move content from annex to direct file. -}
thawContentDir loc
updateInodeCache k loc updateInodeCache k loc
void $ addAssociatedFile k f void $ addAssociatedFile k f
thawContent loc modifyContent loc $ do
replaceFile f $ liftIO . moveFile loc thawContent loc
replaceFile f $ liftIO . moveFile loc
fromdirect loc = do fromdirect loc = do
replaceFile f $ replaceFile f $
liftIO . void . copyFileExternal loc liftIO . void . copyFileExternal loc

View file

@ -13,12 +13,14 @@ module Annex.Perms (
createContentDir, createContentDir,
freezeContentDir, freezeContentDir,
thawContentDir, thawContentDir,
modifyContent,
) where ) where
import Common.Annex import Common.Annex
import Utility.FileMode import Utility.FileMode
import Git.SharedRepository import Git.SharedRepository
import qualified Annex import qualified Annex
import Annex.Exception
import Config import Config
import System.Posix.Types import System.Posix.Types
@ -103,3 +105,13 @@ createContentDir dest = do
liftIO $ allowWrite dir liftIO $ allowWrite dir
where where
dir = parentDir dest dir = parentDir dest
{- 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. -}
modifyContent :: FilePath -> Annex a -> Annex a
modifyContent f a = do
createContentDir f -- also thaws it
v <- tryAnnex a
freezeContentDir f
either throwAnnex return v

View file

@ -218,9 +218,10 @@ 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 && not direct) $ do file <- calcRepo $ gitAnnexLocation key
file <- calcRepo $ gitAnnexLocation key when (present && not direct) $
freezeContent file freezeContent file
whenM (liftIO $ doesDirectoryExist $ parentDir file) $
freezeContentDir file freezeContentDir file
{- In direct mode, modified files will show up as not present, {- In direct mode, modified files will show up as not present,

View file

@ -20,9 +20,9 @@ import Config
import qualified Annex import qualified Annex
import Annex.Direct import Annex.Direct
import Annex.Content import Annex.Content
import Annex.Content.Direct
import Annex.CatFile import Annex.CatFile
import Annex.Version import Annex.Version
import Annex.Perms
import Annex.Exception import Annex.Exception
import Init import Init
import qualified Command.Add import qualified Command.Add
@ -77,7 +77,8 @@ perform = do
Just s Just s
| isSymbolicLink s -> void $ flip whenAnnexed f $ | isSymbolicLink s -> void $ flip whenAnnexed f $
\_ (k, _) -> do \_ (k, _) -> do
cleandirect k removeInodeCache k
removeAssociatedFiles k
return Nothing return Nothing
| otherwise -> | otherwise ->
maybe noop (fromdirect f) maybe noop (fromdirect f)
@ -87,8 +88,8 @@ perform = do
fromdirect f k = do fromdirect f k = do
showStart "indirect" f showStart "indirect" f
thawContentDir =<< calcRepo (gitAnnexLocation k) removeInodeCache k
cleandirect k -- clean before content directory gets frozen removeAssociatedFiles k
whenM (liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f) $ do whenM (liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f) $ do
v <-tryAnnexIO (moveAnnex k f) v <-tryAnnexIO (moveAnnex k f)
case v of case v of
@ -103,10 +104,6 @@ perform = do
warnlocked e = do warnlocked e = do
warning $ show e warning $ show e
warning "leaving this file as-is; correct this problem and run git annex add on it" warning "leaving this file as-is; correct this problem and run git annex add on it"
cleandirect k = do
liftIO . nukeFile =<< calcRepo (gitAnnexInodeCache k)
liftIO . nukeFile =<< calcRepo (gitAnnexMapping k)
cleanup :: CommandCleanup cleanup :: CommandCleanup
cleanup = do cleanup = do

4
debian/changelog vendored
View file

@ -31,6 +31,10 @@ git-annex (5.20131102) UNRELEASED; urgency=low
with a directory. An ordering problem caused the directory to not get with a directory. An ordering problem caused the directory to not get
created in this case. created in this case.
Thanks to Tim for the test cases. Thanks to Tim for the test cases.
* Direct mode .git/annex/objects directories are no longer left writable,
because that allowed writing to symlinks of files that are not present,
which followed the link and put bad content in an object location.
* fsck: Fix up .git/annex/object directory permissions.
-- Joey Hess <joeyh@debian.org> Wed, 06 Nov 2013 16:14:14 -0400 -- Joey Hess <joeyh@debian.org> Wed, 06 Nov 2013 16:14:14 -0400

View file

@ -43,3 +43,6 @@ remote types: git gcrypt S3 bup directory rsync web webdav glacier hook
Linux ceilingcat 3.11.6-1-ARCH #1 SMP PREEMPT Fri Oct 18 23:22:36 CEST 2013 x86_64 GNU/Linux Linux ceilingcat 3.11.6-1-ARCH #1 SMP PREEMPT Fri Oct 18 23:22:36 CEST 2013 x86_64 GNU/Linux
"""]] """]]
> [[fixed|done]]; direct mode now freezes the content directory as indirect
> mode already did. fsck will fix up the permissions too. --[[Joey]]