more OsPath conversion (475/749)

Sponsored-by: Nicholas Golder-Manning
This commit is contained in:
Joey Hess 2025-02-05 12:14:56 -04:00
parent 7805cd89ad
commit b28433072c
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
5 changed files with 153 additions and 154 deletions

View file

@ -17,7 +17,6 @@ module Remote.Directory (
import qualified Data.Map as M
import qualified Data.List.NonEmpty as NE
import qualified System.FilePath.ByteString as P
import Data.Default
import System.PosixCompat.Files (isRegularFile, deviceID)
#ifndef mingw32_HOST_OS
@ -132,11 +131,11 @@ gen r u rc gc rs = do
, config = c
, getRepo = return r
, gitconfig = gc
, localpath = Just dir'
, localpath = Just dir
, readonly = False
, appendonly = False
, untrustworthy = False
, availability = checkPathAvailability True dir'
, availability = checkPathAvailability True dir
, remotetype = remote
, mkUnavailable = gen r u rc
(gc { remoteAnnexDirectory = Just "/dev/null" }) rs
@ -146,8 +145,9 @@ gen r u rc gc rs = do
, remoteStateHandle = rs
}
where
dir = toRawFilePath dir'
dir' = fromMaybe (giveup "missing directory") (remoteAnnexDirectory gc)
dir = toOsPath dir'
dir' = fromMaybe (giveup "missing directory")
(remoteAnnexDirectory gc)
directorySetup :: SetupStage -> Maybe UUID -> Maybe CredPair -> RemoteConfig -> RemoteGitConfig -> Annex (RemoteConfig, UUID)
directorySetup _ mu _ c gc = do
@ -155,43 +155,41 @@ directorySetup _ mu _ c gc = do
-- verify configuration is sane
let dir = maybe (giveup "Specify directory=") fromProposedAccepted $
M.lookup directoryField c
absdir <- liftIO $ fromRawFilePath <$> absPath (toRawFilePath dir)
absdir <- liftIO $ absPath (toOsPath dir)
liftIO $ unlessM (doesDirectoryExist absdir) $
giveup $ "Directory does not exist: " ++ absdir
giveup $ "Directory does not exist: " ++ fromOsPath absdir
(c', _encsetup) <- encryptionSetup c gc
-- The directory is stored in git config, not in this remote's
-- persistent state, so it can vary between hosts.
gitConfigSpecialRemote u c' [("directory", absdir)]
gitConfigSpecialRemote u c' [("directory", fromOsPath absdir)]
return (M.delete directoryField c', u)
{- Locations to try to access a given Key in the directory.
- We try more than one since we used to write to different hash
- directories. -}
locations :: RawFilePath -> Key -> NE.NonEmpty RawFilePath
locations d k = NE.map (d P.</>) (keyPaths k)
locations :: OsPath -> Key -> NE.NonEmpty OsPath
locations d k = NE.map (d </>) (keyPaths k)
locations' :: RawFilePath -> Key -> [RawFilePath]
locations' :: OsPath -> Key -> [OsPath]
locations' d k = NE.toList (locations d k)
{- Returns the location of a Key in the directory. If the key is
- present, returns the location that is actually used, otherwise
- returns the first, default location. -}
getLocation :: RawFilePath -> Key -> IO RawFilePath
getLocation :: OsPath -> Key -> IO OsPath
getLocation d k = do
let locs = locations d k
fromMaybe (NE.head locs)
<$> firstM (doesFileExist . fromRawFilePath)
(NE.toList locs)
fromMaybe (NE.head locs) <$> firstM doesFileExist (NE.toList locs)
{- Directory where the file(s) for a key are stored. -}
storeDir :: RawFilePath -> Key -> RawFilePath
storeDir d k = P.addTrailingPathSeparator $
d P.</> hashDirLower def k P.</> keyFile k
storeDir :: OsPath -> Key -> OsPath
storeDir d k = addTrailingPathSeparator $
d </> hashDirLower def k </> keyFile k
{- Check if there is enough free disk space in the remote's directory to
- store the key. Note that the unencrypted key size is checked. -}
storeKeyM :: RawFilePath -> ChunkConfig -> CopyCoWTried -> Storer
storeKeyM :: OsPath -> ChunkConfig -> CopyCoWTried -> Storer
storeKeyM d chunkconfig cow k c m =
ifM (checkDiskSpaceDirectory d k)
( do
@ -203,16 +201,16 @@ storeKeyM d chunkconfig cow k c m =
store = case chunkconfig of
LegacyChunks chunksize ->
let go _k b p = liftIO $ Legacy.store
(fromRawFilePath d)
(fromOsPath d)
chunksize
(finalizeStoreGeneric d)
k b p
(fromRawFilePath tmpdir)
(fromRawFilePath destdir)
(fromOsPath tmpdir)
(fromOsPath destdir)
in byteStorer go k c m
NoChunks ->
let go _k src p = liftIO $ do
void $ fileCopier cow src tmpf p Nothing
void $ fileCopier cow (fromOsPath src) (fromOsPath tmpf) p Nothing
finalizeStoreGeneric d tmpdir destdir
in fileStorer go k c m
_ ->
@ -221,60 +219,58 @@ storeKeyM d chunkconfig cow k c m =
finalizeStoreGeneric d tmpdir destdir
in byteStorer go k c m
tmpdir = P.addTrailingPathSeparator $ d P.</> "tmp" P.</> kf
tmpf = fromRawFilePath tmpdir </> fromRawFilePath kf
tmpdir = addTrailingPathSeparator $ d </> literalOsPath "tmp" </> kf
tmpf = tmpdir </> kf
kf = keyFile k
destdir = storeDir d k
checkDiskSpaceDirectory :: RawFilePath -> Key -> Annex Bool
checkDiskSpaceDirectory :: OsPath -> Key -> Annex Bool
checkDiskSpaceDirectory d k = do
annexdir <- fromRepo gitAnnexObjectDir
samefilesystem <- liftIO $ catchDefaultIO False $
(\a b -> deviceID a == deviceID b)
<$> R.getSymbolicLinkStatus d
<*> R.getSymbolicLinkStatus annexdir
<$> R.getSymbolicLinkStatus (fromOsPath d)
<*> R.getSymbolicLinkStatus (fromOsPath annexdir)
checkDiskSpace Nothing (Just d) k 0 samefilesystem
{- Passed a temp directory that contains the files that should be placed
- in the dest directory, moves it into place. Anything already existing
- in the dest directory will be deleted. File permissions will be locked
- down. -}
finalizeStoreGeneric :: RawFilePath -> RawFilePath -> RawFilePath -> IO ()
finalizeStoreGeneric :: OsPath -> OsPath -> OsPath -> IO ()
finalizeStoreGeneric d tmp dest = do
removeDirGeneric False d dest
createDirectoryUnder [d] (parentDir dest)
renameDirectory (fromRawFilePath tmp) dest'
renameDirectory tmp dest
-- may fail on some filesystems
void $ tryIO $ do
mapM_ preventWrite =<< dirContents dest
preventWrite dest
where
dest' = fromRawFilePath dest
retrieveKeyFileM :: RawFilePath -> ChunkConfig -> CopyCoWTried -> Retriever
retrieveKeyFileM :: OsPath -> ChunkConfig -> CopyCoWTried -> Retriever
retrieveKeyFileM d (LegacyChunks _) _ = Legacy.retrieve locations' d
retrieveKeyFileM d NoChunks cow = fileRetriever' $ \dest k p iv -> do
src <- liftIO $ fromRawFilePath <$> getLocation d k
void $ liftIO $ fileCopier cow src (fromRawFilePath dest) p iv
src <- liftIO $ getLocation d k
void $ liftIO $ fileCopier cow (fromOsPath src) (fromOsPath dest) p iv
retrieveKeyFileM d _ _ = byteRetriever $ \k sink ->
sink =<< liftIO (F.readFile . toOsPath =<< getLocation d k)
sink =<< liftIO (F.readFile =<< getLocation d k)
retrieveKeyFileCheapM :: RawFilePath -> ChunkConfig -> Maybe (Key -> AssociatedFile -> FilePath -> Annex ())
retrieveKeyFileCheapM :: OsPath -> ChunkConfig -> Maybe (Key -> AssociatedFile -> OsPath -> Annex ())
-- no cheap retrieval possible for chunks
retrieveKeyFileCheapM _ (UnpaddedChunks _) = Nothing
retrieveKeyFileCheapM _ (LegacyChunks _) = Nothing
#ifndef mingw32_HOST_OS
retrieveKeyFileCheapM d NoChunks = Just $ \k _af f -> liftIO $ do
file <- fromRawFilePath <$> (absPath =<< getLocation d k)
file <- absPath =<< getLocation d k
ifM (doesFileExist file)
( R.createSymbolicLink (toRawFilePath file) (toRawFilePath f)
( R.createSymbolicLink (fromOsPath file) (fromOsPath f)
, giveup "content file not present in remote"
)
#else
retrieveKeyFileCheapM _ _ = Nothing
#endif
removeKeyM :: RawFilePath -> Remover
removeKeyM :: OsPath -> Remover
removeKeyM d _proof k = liftIO $ removeDirGeneric True d (storeDir d k)
{- Removes the directory, which must be located under the topdir.
@ -291,7 +287,7 @@ removeKeyM d _proof k = liftIO $ removeDirGeneric True d (storeDir d k)
- can also be removed. Failure to remove such a directory is not treated
- as an error.
-}
removeDirGeneric :: Bool -> RawFilePath -> RawFilePath -> IO ()
removeDirGeneric :: Bool -> OsPath -> OsPath -> IO ()
removeDirGeneric removeemptyparents topdir dir = do
void $ tryIO $ allowWrite dir
#ifdef mingw32_HOST_OS
@ -299,102 +295,100 @@ removeDirGeneric removeemptyparents topdir dir = do
- before it can delete them. -}
void $ tryIO $ mapM_ allowWrite =<< dirContents dir
#endif
tryNonAsync (removeDirectoryRecursive dir') >>= \case
tryNonAsync (removeDirectoryRecursive dir) >>= \case
Right () -> return ()
Left e ->
unlessM (doesDirectoryExist topdir' <&&> (not <$> doesDirectoryExist dir')) $
unlessM (doesDirectoryExist topdir <&&> (not <$> doesDirectoryExist dir)) $
throwM e
when removeemptyparents $ do
subdir <- relPathDirToFile topdir (P.takeDirectory dir)
goparents (Just (P.takeDirectory subdir)) (Right ())
subdir <- relPathDirToFile topdir (takeDirectory dir)
goparents (Just (takeDirectory subdir)) (Right ())
where
goparents _ (Left _e) = return ()
goparents Nothing _ = return ()
goparents (Just subdir) _ = do
let d = topdir' </> fromRawFilePath subdir
let d = topdir </> subdir
goparents (upFrom subdir) =<< tryIO (removeDirectory d)
dir' = fromRawFilePath dir
topdir' = fromRawFilePath topdir
checkPresentM :: RawFilePath -> ChunkConfig -> CheckPresent
checkPresentM :: OsPath -> ChunkConfig -> CheckPresent
checkPresentM d (LegacyChunks _) k = Legacy.checkKey d locations' k
checkPresentM d _ k = checkPresentGeneric d (locations' d k)
checkPresentGeneric :: RawFilePath -> [RawFilePath] -> Annex Bool
checkPresentGeneric :: OsPath -> [OsPath] -> Annex Bool
checkPresentGeneric d ps = checkPresentGeneric' d $
liftIO $ anyM (doesFileExist . fromRawFilePath) ps
liftIO $ anyM doesFileExist ps
checkPresentGeneric' :: RawFilePath -> Annex Bool -> Annex Bool
checkPresentGeneric' :: OsPath -> Annex Bool -> Annex Bool
checkPresentGeneric' d check = ifM check
( return True
, ifM (liftIO $ doesDirectoryExist (fromRawFilePath d))
, ifM (liftIO $ doesDirectoryExist d)
( return False
, giveup $ "directory " ++ fromRawFilePath d ++ " is not accessible"
, giveup $ "directory " ++ fromOsPath d ++ " is not accessible"
)
)
storeExportM :: RawFilePath -> CopyCoWTried -> FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex ()
storeExportM :: OsPath -> CopyCoWTried -> OsPath -> Key -> ExportLocation -> MeterUpdate -> Annex ()
storeExportM d cow src _k loc p = do
liftIO $ createDirectoryUnder [d] (P.takeDirectory dest)
liftIO $ createDirectoryUnder [d] (takeDirectory dest)
-- Write via temp file so that checkPresentGeneric will not
-- see it until it's fully stored.
viaTmp go (toOsPath dest) ()
viaTmp go dest ()
where
dest = exportPath d loc
go tmp () = void $ liftIO $ fileCopier cow src (fromRawFilePath (fromOsPath tmp)) p Nothing
go tmp () = void $ liftIO $
fileCopier cow (fromOsPath src) (fromOsPath tmp) p Nothing
retrieveExportM :: RawFilePath -> CopyCoWTried -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Verification
retrieveExportM :: OsPath -> CopyCoWTried -> Key -> ExportLocation -> OsPath -> MeterUpdate -> Annex Verification
retrieveExportM d cow k loc dest p =
verifyKeyContentIncrementally AlwaysVerify k $ \iv ->
void $ liftIO $ fileCopier cow src dest p iv
void $ liftIO $ fileCopier cow src (fromOsPath dest) p iv
where
src = fromRawFilePath $ exportPath d loc
src = fromOsPath $ exportPath d loc
removeExportM :: RawFilePath -> Key -> ExportLocation -> Annex ()
removeExportM :: OsPath -> Key -> ExportLocation -> Annex ()
removeExportM d _k loc = liftIO $ do
removeWhenExistsWith R.removeLink src
removeWhenExistsWith removeFile src
removeExportLocation d loc
where
src = exportPath d loc
checkPresentExportM :: RawFilePath -> Key -> ExportLocation -> Annex Bool
checkPresentExportM :: OsPath -> Key -> ExportLocation -> Annex Bool
checkPresentExportM d _k loc =
checkPresentGeneric d [exportPath d loc]
renameExportM :: RawFilePath -> Key -> ExportLocation -> ExportLocation -> Annex (Maybe ())
renameExportM :: OsPath -> Key -> ExportLocation -> ExportLocation -> Annex (Maybe ())
renameExportM d _k oldloc newloc = liftIO $ do
createDirectoryUnder [d] (P.takeDirectory dest)
renameFile (fromRawFilePath src) (fromRawFilePath dest)
createDirectoryUnder [d] (takeDirectory dest)
renameFile src dest
removeExportLocation d oldloc
return (Just ())
where
src = exportPath d oldloc
dest = exportPath d newloc
exportPath :: RawFilePath -> ExportLocation -> RawFilePath
exportPath d loc = d P.</> fromExportLocation loc
exportPath :: OsPath -> ExportLocation -> OsPath
exportPath d loc = d </> fromExportLocation loc
{- Removes the ExportLocation's parent directory and its parents, so long as
- they're empty, up to but not including the topdir. -}
removeExportLocation :: RawFilePath -> ExportLocation -> IO ()
removeExportLocation :: OsPath -> ExportLocation -> IO ()
removeExportLocation topdir loc =
go (Just $ P.takeDirectory $ fromExportLocation loc) (Right ())
go (Just $ takeDirectory $ fromExportLocation loc) (Right ())
where
go _ (Left _e) = return ()
go Nothing _ = return ()
go (Just loc') _ =
let p = fromRawFilePath $ exportPath topdir $
mkExportLocation loc'
let p = exportPath topdir $ mkExportLocation loc'
in go (upFrom loc') =<< tryIO (removeDirectory p)
listImportableContentsM :: IgnoreInodes -> RawFilePath -> Annex (Maybe (ImportableContentsChunkable Annex (ContentIdentifier, ByteSize)))
listImportableContentsM :: IgnoreInodes -> OsPath -> Annex (Maybe (ImportableContentsChunkable Annex (ContentIdentifier, ByteSize)))
listImportableContentsM ii dir = liftIO $ do
l' <- mapM go =<< dirContentsRecursiveSkipping (const False) False dir
return $ Just $ ImportableContentsComplete $
ImportableContents (catMaybes l') []
where
go f = do
st <- R.getSymbolicLinkStatus f
st <- R.getSymbolicLinkStatus (fromOsPath f)
mkContentIdentifier ii f st >>= \case
Nothing -> return Nothing
Just cid -> do
@ -408,7 +402,7 @@ newtype IgnoreInodes = IgnoreInodes Bool
-- and also normally the inode, unless ignoreinodes=yes.
--
-- If the file is not a regular file, this will return Nothing.
mkContentIdentifier :: IgnoreInodes -> RawFilePath -> FileStatus -> IO (Maybe ContentIdentifier)
mkContentIdentifier :: IgnoreInodes -> OsPath -> FileStatus -> IO (Maybe ContentIdentifier)
mkContentIdentifier (IgnoreInodes ii) f st =
liftIO $ fmap (ContentIdentifier . encodeBS . showInodeCache)
<$> if ii
@ -434,25 +428,25 @@ guardSameContentIdentifiers cont olds (Just new)
let ic' = replaceInode 0 ic
in ContentIdentifier (encodeBS (showInodeCache ic'))
importKeyM :: IgnoreInodes -> RawFilePath -> ExportLocation -> ContentIdentifier -> ByteSize -> MeterUpdate -> Annex (Maybe Key)
importKeyM :: IgnoreInodes -> OsPath -> ExportLocation -> ContentIdentifier -> ByteSize -> MeterUpdate -> Annex (Maybe Key)
importKeyM ii dir loc cid sz p = do
backend <- chooseBackend f
unsizedk <- fst <$> genKey ks p backend
let k = alterKey unsizedk $ \kd -> kd
{ keySize = keySize kd <|> Just sz }
currcid <- liftIO $ mkContentIdentifier ii absf
=<< R.getSymbolicLinkStatus absf
=<< R.getSymbolicLinkStatus (fromOsPath absf)
guardSameContentIdentifiers (return (Just k)) [cid] currcid
where
f = fromExportLocation loc
absf = dir P.</> f
absf = dir </> f
ks = KeySource
{ keyFilename = f
, contentLocation = absf
, inodeCache = Nothing
}
retrieveExportWithContentIdentifierM :: IgnoreInodes -> RawFilePath -> CopyCoWTried -> ExportLocation -> [ContentIdentifier] -> FilePath -> Either Key (Annex Key) -> MeterUpdate -> Annex (Key, Verification)
retrieveExportWithContentIdentifierM :: IgnoreInodes -> OsPath -> CopyCoWTried -> ExportLocation -> [ContentIdentifier] -> OsPath -> Either Key (Annex Key) -> MeterUpdate -> Annex (Key, Verification)
retrieveExportWithContentIdentifierM ii dir cow loc cids dest gk p =
case gk of
Right mkkey -> do
@ -464,11 +458,11 @@ retrieveExportWithContentIdentifierM ii dir cow loc cids dest gk p =
return (k, v)
where
f = exportPath dir loc
f' = fromRawFilePath f
f' = fromOsPath f
go iv = precheck (docopy iv)
docopy iv = ifM (liftIO $ tryCopyCoW cow f' dest p)
docopy iv = ifM (liftIO $ tryCopyCoW cow (fromOsPath f) (fromOsPath dest) p)
( postcheckcow (liftIO $ maybe noop unableIncrementalVerifier iv)
, docopynoncow iv
)
@ -477,7 +471,7 @@ retrieveExportWithContentIdentifierM ii dir cow loc cids dest gk p =
#ifndef mingw32_HOST_OS
let open = do
-- Need a duplicate fd for the post check.
fd <- openFdWithMode f ReadOnly Nothing defaultFileFlags
fd <- openFdWithMode f' ReadOnly Nothing defaultFileFlags
dupfd <- dup fd
h <- fdToHandle fd
return (h, dupfd)
@ -490,7 +484,7 @@ retrieveExportWithContentIdentifierM ii dir cow loc cids dest gk p =
let close = hClose
bracketIO open close $ \h -> do
#endif
liftIO $ fileContentCopier h dest p iv
liftIO $ fileContentCopier h (fromOsPath dest) p iv
#ifndef mingw32_HOST_OS
postchecknoncow dupfd (return ())
#else
@ -501,7 +495,7 @@ retrieveExportWithContentIdentifierM ii dir cow loc cids dest gk p =
-- content.
precheck cont = guardSameContentIdentifiers cont cids
=<< liftIO . mkContentIdentifier ii f
=<< liftIO (R.getSymbolicLinkStatus f)
=<< liftIO (R.getSymbolicLinkStatus f')
-- Check after copy, in case the file was changed while it was
-- being copied.
@ -525,7 +519,7 @@ retrieveExportWithContentIdentifierM ii dir cow loc cids dest gk p =
#ifndef mingw32_HOST_OS
=<< getFdStatus fd
#else
=<< R.getSymbolicLinkStatus f
=<< R.getSymbolicLinkStatus f'
#endif
guardSameContentIdentifiers cont cids currcid
@ -536,37 +530,37 @@ retrieveExportWithContentIdentifierM ii dir cow loc cids dest gk p =
-- restored to the original content before this check.
postcheckcow cont = do
currcid <- liftIO $ mkContentIdentifier ii f
=<< R.getSymbolicLinkStatus f
=<< R.getSymbolicLinkStatus f'
guardSameContentIdentifiers cont cids currcid
storeExportWithContentIdentifierM :: IgnoreInodes -> RawFilePath -> CopyCoWTried -> FilePath -> Key -> ExportLocation -> [ContentIdentifier] -> MeterUpdate -> Annex ContentIdentifier
storeExportWithContentIdentifierM :: IgnoreInodes -> OsPath -> CopyCoWTried -> OsPath -> Key -> ExportLocation -> [ContentIdentifier] -> MeterUpdate -> Annex ContentIdentifier
storeExportWithContentIdentifierM ii dir cow src _k loc overwritablecids p = do
liftIO $ createDirectoryUnder [dir] destdir
withTmpFileIn (toOsPath destdir) template $ \tmpf tmph -> do
withTmpFileIn destdir template $ \tmpf tmph -> do
let tmpf' = fromOsPath tmpf
liftIO $ hClose tmph
void $ liftIO $ fileCopier cow src (fromRawFilePath tmpf') p Nothing
resetAnnexFilePerm tmpf'
liftIO (R.getSymbolicLinkStatus tmpf') >>= liftIO . mkContentIdentifier ii tmpf' >>= \case
void $ liftIO $ fileCopier cow (fromOsPath src) (fromOsPath tmpf) p Nothing
resetAnnexFilePerm tmpf
liftIO (R.getSymbolicLinkStatus tmpf') >>= liftIO . mkContentIdentifier ii tmpf >>= \case
Nothing -> giveup "unable to generate content identifier"
Just newcid -> do
checkExportContent ii dir loc
overwritablecids
(giveup "unsafe to overwrite file")
(const $ liftIO $ R.rename tmpf' dest)
(const $ liftIO $ R.rename tmpf' (fromOsPath dest))
return newcid
where
dest = exportPath dir loc
(destdir, base) = P.splitFileName dest
template = relatedTemplate (base <> ".tmp")
(destdir, base) = splitFileName dest
template = relatedTemplate (fromOsPath base <> ".tmp")
removeExportWithContentIdentifierM :: IgnoreInodes -> RawFilePath -> Key -> ExportLocation -> [ContentIdentifier] -> Annex ()
removeExportWithContentIdentifierM :: IgnoreInodes -> OsPath -> Key -> ExportLocation -> [ContentIdentifier] -> Annex ()
removeExportWithContentIdentifierM ii dir k loc removeablecids =
checkExportContent ii dir loc removeablecids (giveup "unsafe to remove modified file") $ \case
DoesNotExist -> return ()
KnownContentIdentifier -> removeExportM dir k loc
checkPresentExportWithContentIdentifierM :: IgnoreInodes -> RawFilePath -> Key -> ExportLocation -> [ContentIdentifier] -> Annex Bool
checkPresentExportWithContentIdentifierM :: IgnoreInodes -> OsPath -> Key -> ExportLocation -> [ContentIdentifier] -> Annex Bool
checkPresentExportWithContentIdentifierM ii dir _k loc knowncids =
checkPresentGeneric' dir $
checkExportContent ii dir loc knowncids (return False) $ \case
@ -590,9 +584,9 @@ data CheckResult = DoesNotExist | KnownContentIdentifier
--
-- So, it suffices to check if the destination file's current
-- content is known, and immediately run the callback.
checkExportContent :: IgnoreInodes -> RawFilePath -> ExportLocation -> [ContentIdentifier] -> Annex a -> (CheckResult -> Annex a) -> Annex a
checkExportContent :: IgnoreInodes -> OsPath -> ExportLocation -> [ContentIdentifier] -> Annex a -> (CheckResult -> Annex a) -> Annex a
checkExportContent ii dir loc knowncids unsafe callback =
tryWhenExists (liftIO $ R.getSymbolicLinkStatus dest) >>= \case
tryWhenExists (liftIO $ R.getSymbolicLinkStatus (fromOsPath dest)) >>= \case
Just destst
| not (isRegularFile destst) -> unsafe
| otherwise -> catchDefaultIO Nothing (liftIO $ mkContentIdentifier ii dest destst) >>= \case