Special remotes now all rollback storage of keys that get modified during the transfer, which can happen in direct mode.

This commit is contained in:
Joey Hess 2013-01-09 18:42:29 -04:00
parent b3559d8eae
commit 1bc49b7158
11 changed files with 63 additions and 44 deletions

View file

@ -262,21 +262,11 @@ replaceFile file a = do
{- Runs an action to transfer an object's content. {- Runs an action to transfer an object's content.
- -
- In direct mode, it's possible for the file to change as it's being sent. - In direct mode, it's possible for the file to change as it's being sent.
- If this happens, returns False. Currently, an arbitrary amount of bad - If this happens, runs the rollback action and returns False. The
- data may be sent when this occurs. The send is not retried even if - rollback action should remove the data that was transferred for the key.
- another file is known to have the same content; the action may not be
- idempotent.
-
- Since objects changing as they're transferred is a somewhat unusual
- situation, and since preventing writes to the file would be expensive,
- annoying or both, we instead detect the situation after the affect,
- and fail. Thus, it's up to the caller to detect a failure and take
- appropriate action. Such as, for example, ensuring that the bad
- data that was sent does not get installed into the annex it's being
- sent to.
-} -}
sendAnnex :: Key -> (FilePath -> Annex Bool) -> Annex Bool sendAnnex :: Key -> (Annex ()) -> (FilePath -> Annex Bool) -> Annex Bool
sendAnnex key a = withObjectLoc key sendobject senddirect sendAnnex key rollback a = withObjectLoc key sendobject senddirect
where where
sendobject = a sendobject = a
senddirect [] = return False senddirect [] = return False
@ -287,8 +277,12 @@ sendAnnex key a = withObjectLoc key sendobject senddirect
( do ( do
r <- sendobject f r <- sendobject f
-- see if file changed while it was being sent -- see if file changed while it was being sent
ok <- compareCache f cache ifM (compareCache f cache)
return (r && ok) ( return r
, do
rollback
return False
)
, senddirect fs , senddirect fs
) )

View file

@ -24,11 +24,16 @@ seek = [withKeys start]
start :: Key -> CommandStart start :: Key -> CommandStart
start key = ifM (inAnnex key) start key = ifM (inAnnex key)
( fieldTransfer Upload key $ \_p -> ( fieldTransfer Upload key $ \_p ->
sendAnnex key $ liftIO . rsyncServerSend sendAnnex key rollback $ liftIO . rsyncServerSend
, do , do
warning "requested key is not present" warning "requested key is not present"
liftIO exitFailure liftIO exitFailure
) )
where
{- No need to do any rollback; when sendAnnex fails, a nonzero
- exit will be propigated, and the remote will know the transfer
- failed. -}
rollback = noop
fieldTransfer :: Direction -> Key -> (MeterUpdate -> Annex Bool) -> CommandStart fieldTransfer :: Direction -> Key -> (MeterUpdate -> Annex Bool) -> CommandStart
fieldTransfer direction key a = do fieldTransfer direction key a = do

View file

@ -121,16 +121,17 @@ bupSplitParams r buprepo k src = do
(os ++ [Param "-n", Param (bupRef k)] ++ src) (os ++ [Param "-n", Param (bupRef k)] ++ src)
store :: Remote -> BupRepo -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: Remote -> BupRepo -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store r buprepo k _f _p = sendAnnex k $ \src -> do store r buprepo k _f _p = sendAnnex k (rollback k buprepo) $ \src -> do
params <- bupSplitParams r buprepo k [File src] params <- bupSplitParams r buprepo k [File src]
liftIO $ boolSystem "bup" params liftIO $ boolSystem "bup" params
storeEncrypted :: Remote -> BupRepo -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted :: Remote -> BupRepo -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted r buprepo (cipher, enck) k _p = sendAnnex k $ \src -> do storeEncrypted r buprepo (cipher, enck) k _p =
params <- bupSplitParams r buprepo enck [] sendAnnex k (rollback enck buprepo) $ \src -> do
liftIO $ catchBoolIO $ params <- bupSplitParams r buprepo enck []
encrypt cipher (feedFile src) $ \h -> liftIO $ catchBoolIO $
pipeBup params (Just h) Nothing encrypt cipher (feedFile src) $ \h ->
pipeBup params (Just h) Nothing
retrieve :: BupRepo -> Key -> AssociatedFile -> FilePath -> Annex Bool retrieve :: BupRepo -> Key -> AssociatedFile -> FilePath -> Annex Bool
retrieve buprepo k _f d = do retrieve buprepo k _f d = do
@ -157,6 +158,20 @@ remove _ = do
warning "content cannot be removed from bup remote" warning "content cannot be removed from bup remote"
return False return False
{- Cannot revert having stored a key in bup, but at least the data for the
- key will be used for deltaing data of other keys stored later.
-
- We can, however, remove the git branch that bup created for the key.
-}
rollback :: Key -> BupRepo -> Annex ()
rollback k bupr = go =<< liftIO (bup2GitRemote bupr)
where
go r
| Git.repoIsUrl r = void $ onBupRemote r boolSystem "git" params
| otherwise = void $ liftIO $ catchMaybeIO $
boolSystem "git" $ Git.Command.gitCommandLine params r
params = [ Params "branch -D", Param (bupRef k) ]
{- Bup does not provide a way to tell if a given dataset is present {- Bup does not provide a way to tell if a given dataset is present
- in a bup repository. One way it to check if the git repository has - in a bup repository. One way it to check if the git repository has
- a branch matching the name (as created by bup split -n). - a branch matching the name (as created by bup split -n).

View file

@ -111,7 +111,7 @@ withStoredFiles :: ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO
withStoredFiles = withCheckedFiles doesFileExist withStoredFiles = withCheckedFiles doesFileExist
store :: FilePath -> ChunkSize -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: FilePath -> ChunkSize -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store d chunksize k _f p = sendAnnex k $ \src -> store d chunksize k _f p = sendAnnex k (void $ remove d k) $ \src ->
metered (Just p) k $ \meterupdate -> metered (Just p) k $ \meterupdate ->
storeHelper d chunksize k $ \dests -> storeHelper d chunksize k $ \dests ->
case chunksize of case chunksize of
@ -125,7 +125,7 @@ store d chunksize k _f p = sendAnnex k $ \src ->
=<< L.readFile src =<< L.readFile src
storeEncrypted :: FilePath -> ChunkSize -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted :: FilePath -> ChunkSize -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted d chunksize (cipher, enck) k p = sendAnnex k $ \src -> storeEncrypted d chunksize (cipher, enck) k p = sendAnnex k (void $ remove d enck) $ \src ->
metered (Just p) k $ \meterupdate -> metered (Just p) k $ \meterupdate ->
storeHelper d chunksize enck $ \dests -> storeHelper d chunksize enck $ \dests ->
encrypt cipher (feedFile src) $ readBytes $ \b -> encrypt cipher (feedFile src) $ readBytes $ \b ->

View file

@ -268,7 +268,7 @@ copyFromRemote r key file dest
-- run copy from perspective of remote -- run copy from perspective of remote
liftIO $ onLocal (repo r) $ do liftIO $ onLocal (repo r) $ do
ensureInitialized ensureInitialized
Annex.Content.sendAnnex key $ \object -> Annex.Content.sendAnnex key noop $ \object ->
upload u key file noRetry $ upload u key file noRetry $
rsyncOrCopyFile params object dest rsyncOrCopyFile params object dest
| Git.repoIsSsh (repo r) = feedprogressback $ \feeder -> | Git.repoIsSsh (repo r) = feedprogressback $ \feeder ->
@ -333,11 +333,11 @@ copyToRemote r key file p
| not $ Git.repoIsUrl (repo r) = | not $ Git.repoIsUrl (repo r) =
guardUsable (repo r) False $ commitOnCleanup r $ copylocal guardUsable (repo r) False $ commitOnCleanup r $ copylocal
| Git.repoIsSsh (repo r) = commitOnCleanup r $ | Git.repoIsSsh (repo r) = commitOnCleanup r $
Annex.Content.sendAnnex key $ \object -> Annex.Content.sendAnnex key noop $ \object ->
rsyncHelper (Just p) =<< rsyncParamsRemote r False key object file rsyncHelper (Just p) =<< rsyncParamsRemote r False key object file
| otherwise = error "copying to non-ssh repo not supported" | otherwise = error "copying to non-ssh repo not supported"
where where
copylocal = Annex.Content.sendAnnex key $ \object -> do copylocal = Annex.Content.sendAnnex key noop $ \object -> do
let params = rsyncParams r let params = rsyncParams r
u <- getUUID u <- getUUID
-- run copy from perspective of remote -- run copy from perspective of remote

View file

@ -85,12 +85,12 @@ store r k _f m
| keySize k == Just 0 = do | keySize k == Just 0 = do
warning "Cannot store empty files in Glacier." warning "Cannot store empty files in Glacier."
return False return False
| otherwise = sendAnnex k $ \src -> | otherwise = sendAnnex k (void $ remove r k) $ \src ->
metered (Just m) k $ \meterupdate -> metered (Just m) k $ \meterupdate ->
storeHelper r k $ streamMeteredFile src meterupdate storeHelper r k $ streamMeteredFile src meterupdate
storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted r (cipher, enck) k m = sendAnnex k $ \src -> do storeEncrypted r (cipher, enck) k m = sendAnnex k (void $ remove r enck) $ \src -> do
metered (Just m) k $ \meterupdate -> metered (Just m) k $ \meterupdate ->
storeHelper r enck $ \h -> storeHelper r enck $ \h ->
encrypt cipher (feedFile src) encrypt cipher (feedFile src)

View file

@ -103,12 +103,12 @@ runHook hooktype hook k f a = maybe (return False) run =<< lookupHook hooktype h
) )
store :: String -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: String -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store h k _f _p = sendAnnex k $ \src -> store h k _f _p = sendAnnex k (void $ remove h k) $ \src ->
runHook h "store" k (Just src) $ return True runHook h "store" k (Just src) $ return True
storeEncrypted :: String -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted :: String -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted h (cipher, enck) k _p = withTmp enck $ \tmp -> storeEncrypted h (cipher, enck) k _p = withTmp enck $ \tmp ->
sendAnnex k $ \src -> do sendAnnex k (void $ remove h enck) $ \src -> do
liftIO $ encrypt cipher (feedFile src) $ liftIO $ encrypt cipher (feedFile src) $
readBytes $ L.writeFile tmp readBytes $ L.writeFile tmp
runHook h "store" enck (Just tmp) $ return True runHook h "store" enck (Just tmp) $ return True

View file

@ -101,11 +101,11 @@ rsyncUrls o k = map use annexHashes
f = keyFile k f = keyFile k
store :: RsyncOpts -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: RsyncOpts -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store o k _f p = sendAnnex k $ rsyncSend o p k store o k _f p = sendAnnex k (void $ remove o k) $ rsyncSend o p k
storeEncrypted :: RsyncOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted :: RsyncOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted o (cipher, enck) k p = withTmp enck $ \tmp -> storeEncrypted o (cipher, enck) k p = withTmp enck $ \tmp ->
sendAnnex k $ \src -> do sendAnnex k (void $ remove o enck) $ \src -> do
liftIO $ encrypt cipher (feedFile src) $ liftIO $ encrypt cipher (feedFile src) $
readBytes $ L.writeFile tmp readBytes $ L.writeFile tmp
rsyncSend o p enck tmp rsyncSend o p enck tmp

View file

@ -112,15 +112,16 @@ s3Setup u c = handlehost $ M.lookup "host" c
M.delete "bucket" defaults M.delete "bucket" defaults
store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store r k _f p = s3Action r False $ \(conn, bucket) -> sendAnnex k $ \src -> do store r k _f p = s3Action r False $ \(conn, bucket) ->
res <- storeHelper (conn, bucket) r k p src sendAnnex k (void $ remove r k) $ \src -> do
s3Bool res res <- storeHelper (conn, bucket) r k p src
s3Bool res
storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted r (cipher, enck) k p = s3Action r False $ \(conn, bucket) -> storeEncrypted r (cipher, enck) k p = s3Action r False $ \(conn, bucket) ->
-- To get file size of the encrypted content, have to use a temp file. -- To get file size of the encrypted content, have to use a temp file.
-- (An alternative would be chunking to to a constant size.) -- (An alternative would be chunking to to a constant size.)
withTmp enck $ \tmp -> sendAnnex k $ \src -> do withTmp enck $ \tmp -> sendAnnex k (void $ remove r enck) $ \src -> do
liftIO $ encrypt cipher (feedFile src) $ liftIO $ encrypt cipher (feedFile src) $
readBytes $ L.writeFile tmp readBytes $ L.writeFile tmp
res <- storeHelper (conn, bucket) r enck p tmp res <- storeHelper (conn, bucket) r enck p tmp

View file

@ -83,15 +83,17 @@ webdavSetup u c = do
store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store r k _f p = metered (Just p) k $ \meterupdate -> store r k _f p = metered (Just p) k $ \meterupdate ->
davAction r False $ \(baseurl, user, pass) -> sendAnnex k $ \src -> davAction r False $ \(baseurl, user, pass) ->
liftIO $ withMeteredFile src meterupdate $ sendAnnex k (void $ remove r k) $ \src ->
storeHelper r k baseurl user pass liftIO $ withMeteredFile src meterupdate $
storeHelper r k baseurl user pass
storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted r (cipher, enck) k p = metered (Just p) k $ \meterupdate -> storeEncrypted r (cipher, enck) k p = metered (Just p) k $ \meterupdate ->
davAction r False $ \(baseurl, user, pass) -> sendAnnex k $ \src -> davAction r False $ \(baseurl, user, pass) ->
liftIO $ encrypt cipher (streamMeteredFile src meterupdate) $ sendAnnex k (void $ remove r enck) $ \src ->
readBytes $ storeHelper r enck baseurl user pass liftIO $ encrypt cipher (streamMeteredFile src meterupdate) $
readBytes $ storeHelper r enck baseurl user pass
storeHelper :: Remote -> Key -> DavUrl -> DavUser -> DavPass -> L.ByteString -> IO Bool storeHelper :: Remote -> Key -> DavUrl -> DavUser -> DavPass -> L.ByteString -> IO Bool
storeHelper r k baseurl user pass b = catchBoolIO $ do storeHelper r k baseurl user pass b = catchBoolIO $ do

2
debian/changelog vendored
View file

@ -1,6 +1,8 @@
git-annex (3.20130108) UNRELEASED; urgency=low git-annex (3.20130108) UNRELEASED; urgency=low
* fsck: Better checking of file content in direct mode. * fsck: Better checking of file content in direct mode.
* Special remotes now all rollback storage of keys that get modified
during the transfer, which can happen in direct mode.
-- Joey Hess <joeyh@debian.org> Tue, 08 Jan 2013 12:37:38 -0400 -- Joey Hess <joeyh@debian.org> Tue, 08 Jan 2013 12:37:38 -0400