make linkAnnex detect when the file changes as it's being copied/linked in
authorJoey Hess <joeyh@joeyh.name>
Tue, 22 Dec 2015 19:20:03 +0000 (15:20 -0400)
committerJoey Hess <joeyh@joeyh.name>
Tue, 22 Dec 2015 19:20:03 +0000 (15:20 -0400)
This fixes a race where the modified file ended up in annex/objects, and
the InodeCache stored in the database was for the modified version, so
git-annex didn't know it had gotten modified.

The race could occur when the smudge filter was running; now it gets the
InodeCache before generating the Key, which avoids the race.

Annex/Content.hs
Command/Smudge.hs
Command/Unlock.hs
Upgrade/V5.hs

index 4cd2b02597863a3302945dae9983fc26b8ef2b28..c00863e31029559624a5a687736c921c6bd7f007 100644 (file)
@@ -521,30 +521,43 @@ populatePointerFile k obj f = go =<< isPointerFile f
  - prevent losing the content if the source file is deleted, but does not
  - guard against modifications.
  -}
-linkAnnex :: Key -> FilePath -> Annex LinkAnnexResult
-linkAnnex key src = do
+linkAnnex :: Key -> FilePath -> Maybe InodeCache -> Annex LinkAnnexResult
+linkAnnex key src srcic = do
        dest <- calcRepo (gitAnnexLocation key)
-       modifyContent dest $ linkAnnex' key src dest
+       modifyContent dest $ linkAnnex' key src srcic dest
 
 {- Hard links (or copies) src to dest, one of which should be the
  - annex object. Updates inode cache for src and for dest when it's
  - changed. -}
-linkAnnex' :: Key -> FilePath -> FilePath -> Annex LinkAnnexResult
-linkAnnex' key src dest = 
+linkAnnex' :: Key -> FilePath -> Maybe InodeCache -> FilePath -> Annex LinkAnnexResult
+linkAnnex' _ _ Nothing _ = return LinkAnnexFailed
+linkAnnex' key src (Just srcic) dest = 
        ifM (liftIO $ doesFileExist dest)
                ( do
-                       Database.Keys.storeInodeCaches key [src]
+                       Database.Keys.addInodeCaches key [srcic]
                        return LinkAnnexNoop
                , ifM (linkAnnex'' key src dest)
                        ( do
                                thawContent dest
-                               Database.Keys.storeInodeCaches key [dest, src]
-                               return LinkAnnexOk
-                       , do
-                               Database.Keys.storeInodeCaches key [src]
-                               return LinkAnnexFailed
+                               -- src could have changed while being copied
+                               -- to dest
+                               mcache <- withTSDelta (liftIO . genInodeCache src)
+                               case mcache of
+                                       Just srcic' | compareStrong srcic srcic' -> do
+                                               destic <- withTSDelta (liftIO . genInodeCache dest)
+                                               Database.Keys.addInodeCaches key $
+                                                       catMaybes [destic, Just srcic]
+                                               return LinkAnnexOk
+                                       _ -> do
+                                               liftIO $ nukeFile dest
+                                               failed
+                       , failed
                        )
                )
+  where
+       failed = do
+               Database.Keys.addInodeCaches key [srcic]
+               return LinkAnnexFailed
 
 data LinkAnnexResult = LinkAnnexOk | LinkAnnexFailed | LinkAnnexNoop
 
index e6541bc6d6acea02cc6996f82e634cf61ea5db61..2876326b8b3546059d867df2cf91a1aa6ae8dae2 100644 (file)
@@ -14,6 +14,7 @@ import Annex.Link
 import Annex.MetaData
 import Annex.FileMatcher
 import Annex.InodeSentinal
+import Utility.InodeCache
 import Types.KeySource
 import Backend
 import Logs.Location
@@ -86,7 +87,7 @@ clean file = do
 
 -- If the file being cleaned was hard linked to the old key's annex object,
 -- modifying the file will have caused the object to have the wrong content.
--- Clean up from that, making the 
+-- Clean up from that.
 cleanOldKey :: FilePath -> Key -> Annex ()
 cleanOldKey modifiedfile key = do
        obj <- calcRepo (gitAnnexLocation key)
@@ -99,7 +100,9 @@ cleanOldKey modifiedfile key = do
                case fs' of
                        -- If linkAnnex fails, the file with the content
                        -- is still present, so no need for any recovery.
-                       (f:_) -> void $ linkAnnex key f
+                       (f:_) -> do
+                               ic <- withTSDelta (liftIO . genInodeCache f)
+                               void $ linkAnnex key f ic
                        _ -> lostcontent
   where
        lostcontent = logStatus key InfoMissing
@@ -112,17 +115,18 @@ shouldAnnex file = do
 ingest :: FilePath -> Annex Key
 ingest file = do
        backend <- chooseBackend file
+       ic <- withTSDelta (liftIO . genInodeCache file)
        let source = KeySource
                { keyFilename = file
                , contentLocation = file
-               , inodeCache = Nothing
+               , inodeCache = ic
                }
        k <- fst . fromMaybe (error "failed to generate a key")
                <$> genKey source backend
        -- Hard link (or copy) file content to annex object
        -- to prevent it from being lost when git checks out
        -- a branch not containing this file.
-       r <- linkAnnex k file
+       r <- linkAnnex k file ic
        case r of
                LinkAnnexFailed -> error "Problem adding file to the annex"
                LinkAnnexOk -> logStatus k InfoPresent
index 1cfd4a0b2b86e669457b8c752db545973178eda9..b82f78096b3a9c3341a2c09567d5c02b24118ec2 100644 (file)
@@ -14,6 +14,8 @@ import Annex.CatFile
 import Annex.Version
 import Annex.Link
 import Annex.ReplaceFile
+import Annex.InodeSentinal
+import Utility.InodeCache
 import Utility.CopyFile
 
 cmd :: Command
@@ -51,8 +53,9 @@ start file key = ifM (isJust <$> isAnnexLink file)
 performNew :: FilePath -> Key -> CommandPerform
 performNew dest key = do
        src <- calcRepo (gitAnnexLocation key)
+       srcic <- withTSDelta (liftIO . genInodeCache src)
        replaceFile dest $ \tmp -> do
-               r <- linkAnnex' key src tmp
+               r <- linkAnnex' key src srcic tmp
                case r of
                        LinkAnnexOk -> return ()
                        _ -> error "linkAnnex failed"
index 2073a0150f7bed5fae383494c1d3254b77a1c6c7..f6d18df435a20a1d787765f300c1a6b1d0d7ac03 100644 (file)
@@ -20,6 +20,7 @@ import qualified Git
 import qualified Git.LsFiles
 import qualified Git.Branch
 import Git.FileMode
+import Utility.InodeCache
 
 upgrade :: Bool -> Annex Bool
 upgrade automatic = do
@@ -88,7 +89,8 @@ upgradeDirectWorkTree = do
                -- not populated with it. Since the work tree file
                -- is recorded as an associated file, things will still
                -- work that way, it's just not ideal.
-               void $ linkAnnex k f
+               ic <- withTSDelta (liftIO . genInodeCache f)
+               void $ linkAnnex k f ic
        writepointer f k = liftIO $ do
                nukeFile f
                writeFile f (formatPointer k)