CoW probing
Improved probing when CoW copies can be made between files on the same drive. Now supports CoW between BTRFS subvolumes. And, falls back to rsync instead of using cp when CoW won't work, eg copies between repos on the same EXT4 filesystem. Rather than trying cp --reflink=always for each file copied to a remote, it's tried once and if it fails it falls back to using rsync thereafter for the lifetime of the Remote object. That avoids overhead of calling cp which while small, will add up over a large number of files. This commit was sponsored by Boyd Stephen Smith Jr. on Patreon.
This commit is contained in:
parent
0dc26cd6f1
commit
21ff5e1e5a
6 changed files with 100 additions and 37 deletions
|
@ -1,12 +1,13 @@
|
|||
{- file copying
|
||||
-
|
||||
- Copyright 2010-2014 Joey Hess <id@joeyh.name>
|
||||
- Copyright 2010-2019 Joey Hess <id@joeyh.name>
|
||||
-
|
||||
- License: BSD-2-clause
|
||||
-}
|
||||
|
||||
module Utility.CopyFile (
|
||||
copyFileExternal,
|
||||
copyCoW,
|
||||
createLinkOrCopy,
|
||||
CopyMetaData(..)
|
||||
) where
|
||||
|
@ -22,6 +23,17 @@ data CopyMetaData
|
|||
| CopyAllMetaData
|
||||
deriving (Eq)
|
||||
|
||||
copyMetaDataParams :: CopyMetaData -> [CommandParam]
|
||||
copyMetaDataParams meta = map snd $ filter fst
|
||||
[ (allmeta && BuildInfo.cp_a, Param "-a")
|
||||
, (allmeta && BuildInfo.cp_p && not BuildInfo.cp_a
|
||||
, Param "-p")
|
||||
, (not allmeta && BuildInfo.cp_preserve_timestamps
|
||||
, Param "--preserve=timestamps")
|
||||
]
|
||||
where
|
||||
allmeta = meta == CopyAllMetaData
|
||||
|
||||
{- The cp command is used, because I hate reinventing the wheel,
|
||||
- and because this allows easy access to features like cp --reflink. -}
|
||||
copyFileExternal :: CopyMetaData -> FilePath -> FilePath -> IO Bool
|
||||
|
@ -30,15 +42,33 @@ copyFileExternal meta src dest = do
|
|||
removeFile dest
|
||||
boolSystem "cp" $ params ++ [File src, File dest]
|
||||
where
|
||||
params = map snd $ filter fst
|
||||
[ (BuildInfo.cp_reflink_auto, Param "--reflink=auto")
|
||||
, (allmeta && BuildInfo.cp_a, Param "-a")
|
||||
, (allmeta && BuildInfo.cp_p && not BuildInfo.cp_a
|
||||
, Param "-p")
|
||||
, (not allmeta && BuildInfo.cp_preserve_timestamps
|
||||
, Param "--preserve=timestamps")
|
||||
]
|
||||
allmeta = meta == CopyAllMetaData
|
||||
params
|
||||
| BuildInfo.cp_reflink_supported =
|
||||
Param "--reflink=auto" : copyMetaDataParams meta
|
||||
| otherwise = copyMetaDataParams meta
|
||||
|
||||
{- When a filesystem supports CoW (and cp does), uses it to make
|
||||
- an efficient copy of a file. Otherwise, returns False. -}
|
||||
copyCoW :: CopyMetaData -> FilePath -> FilePath -> IO Bool
|
||||
copyCoW meta src dest
|
||||
| BuildInfo.cp_reflink_supported = do
|
||||
whenM (doesFileExist dest) $
|
||||
removeFile dest
|
||||
-- When CoW is not supported, cp will complain to stderr,
|
||||
-- so have to discard its stderr.
|
||||
ok <- catchBoolIO $ do
|
||||
withQuietOutput createProcessSuccess $
|
||||
proc "cp" $ toCommand $
|
||||
params ++ [File src, File dest]
|
||||
return True
|
||||
-- When CoW is not supported, cp creates the destination
|
||||
-- file but leaves it empty.
|
||||
unless ok $
|
||||
void $ tryIO $ removeFile dest
|
||||
return ok
|
||||
| otherwise = return False
|
||||
where
|
||||
params = Param "--reflink=always" : copyMetaDataParams meta
|
||||
|
||||
{- Create a hard link if the filesystem allows it, and fall back to copying
|
||||
- the file. -}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue