storeExportWithContentIdentifierM for directory special remote
Not sure if my reasoning about the races really holds. It would certianly be possible to better guard against races by using Linux-specific renameat2 with RENAME_EXCHANGE or RENAME_NOREPLACE. Or by using link and relying on it not overwriting existing files -- but that would need a filesystem that supports hard links and directory can be used in filesystems that don't.
This commit is contained in:
parent
1ec9e1494c
commit
88ccfaa78c
2 changed files with 49 additions and 4 deletions
|
@ -387,4 +387,50 @@ retrieveExportWithContentIdentifierM dir loc cid dest mkkey p =
|
||||||
| otherwise = return Nothing
|
| otherwise = return Nothing
|
||||||
|
|
||||||
storeExportWithContentIdentifierM :: FilePath -> FilePath -> Key -> ExportLocation -> [ContentIdentifier] -> MeterUpdate -> Annex (Maybe ContentIdentifier)
|
storeExportWithContentIdentifierM :: FilePath -> FilePath -> Key -> ExportLocation -> [ContentIdentifier] -> MeterUpdate -> Annex (Maybe ContentIdentifier)
|
||||||
storeExportWithContentIdentifierM dir = error "TODO"
|
storeExportWithContentIdentifierM dir src _k loc overwritablecids p =
|
||||||
|
liftIO $ catchDefaultIO Nothing $ do
|
||||||
|
createDirectoryIfMissing True destdir
|
||||||
|
docopy checkoverwrite
|
||||||
|
where
|
||||||
|
dest = dir </> fromExportLocation loc
|
||||||
|
(destdir, base) = splitFileName dest
|
||||||
|
template = relatedTemplate (base ++ ".tmp")
|
||||||
|
|
||||||
|
docopy cont = withTmpFileIn destdir template $ \tmpf tmph -> do
|
||||||
|
withMeteredFile src p (L.hPut tmph)
|
||||||
|
hFlush tmph
|
||||||
|
getFileStatus tmpf >>= mkContentIdentifier tmpf >>= \case
|
||||||
|
Nothing -> return Nothing
|
||||||
|
Just newcid -> cont newcid $ do
|
||||||
|
rename tmpf dest
|
||||||
|
return (Just newcid)
|
||||||
|
|
||||||
|
-- If the destination file already exists, it should only
|
||||||
|
-- be overwritten when its ContentIdentifier is in overwritablecids
|
||||||
|
-- or is the same as the ContentIdentifier of the replacement.
|
||||||
|
--
|
||||||
|
-- This should avoid races to the extent possible. However,
|
||||||
|
-- if something has the destination file open for write,
|
||||||
|
-- it could write to it after it's been overwritten with the new
|
||||||
|
-- content, and its write would be lost, and we don't need to
|
||||||
|
-- detect that. (In similar situations, git doesn't either!)
|
||||||
|
--
|
||||||
|
-- It follows that if something is written to the destination file
|
||||||
|
-- shortly before, it's acceptable to overwrite anyway, as that's
|
||||||
|
-- nearly indistinguishable from the above case.
|
||||||
|
--
|
||||||
|
-- So, it suffices to check if the destination file's current
|
||||||
|
-- content can be overwritten, and immediately overwrite it.
|
||||||
|
checkoverwrite newcid finalize = do
|
||||||
|
destst <- getFileStatus dest
|
||||||
|
if isRegularFile destst
|
||||||
|
then catchDefaultIO Nothing (mkContentIdentifier dest destst) >>= \case
|
||||||
|
Just destcid
|
||||||
|
| destcid `elem` overwritablecids ->
|
||||||
|
finalize
|
||||||
|
| destcid == newcid -> finalize
|
||||||
|
-- dest exists with other content
|
||||||
|
| otherwise -> return Nothing
|
||||||
|
-- dest does not exist, not overwriting
|
||||||
|
Nothing -> finalize
|
||||||
|
else return Nothing
|
||||||
|
|
|
@ -274,9 +274,8 @@ data ImportActions a = ImportActions
|
||||||
--
|
--
|
||||||
-- Since other things can modify the same file on the special
|
-- Since other things can modify the same file on the special
|
||||||
-- remote, this must take care to not overwrite such modifications,
|
-- remote, this must take care to not overwrite such modifications,
|
||||||
-- and only overwrite a file that has the same ContentIdentifier
|
-- and only overwrite a file that has one of the ContentIdentifiers
|
||||||
-- as was passed to it, unless listContents can recover an
|
-- passed to it, unless listContents can recover an overwritten file.
|
||||||
-- overwritten file.
|
|
||||||
--
|
--
|
||||||
-- Also, since there can be concurrent writers, the implementation
|
-- Also, since there can be concurrent writers, the implementation
|
||||||
-- needs to make sure that the ContentIdentifier it returns
|
-- needs to make sure that the ContentIdentifier it returns
|
||||||
|
|
Loading…
Reference in a new issue