summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2011-11-22 18:20:55 -0400
committerGravatar Joey Hess <joey@kitenet.net>2011-11-22 18:21:28 -0400
commit75a590bdd893e579c5e375e5ad797022f5847496 (patch)
tree38c2e9f62d05b811ca025ba604a9898913dfdcb7
parentfd81b5047b314ecc96b34e0c9f017f8f5a62f072 (diff)
Put a workaround in the directory special remote for strange behavior with VFAT filesystems on Linux (mounted with shortname=mixed)
-rw-r--r--Remote/Directory.hs73
-rw-r--r--debian/changelog7
-rw-r--r--doc/bugs/directory_remote_and_case_sensitivity_on_FAT.mdwn23
3 files changed, 76 insertions, 27 deletions
diff --git a/Remote/Directory.hs b/Remote/Directory.hs
index b592f41ff..cadd5e759 100644
--- a/Remote/Directory.hs
+++ b/Remote/Directory.hs
@@ -62,56 +62,81 @@ directorySetup u c = do
gitConfigSpecialRemote u c' "directory" dir
return $ M.delete "directory" c'
-dirKey :: FilePath -> Key -> FilePath
-dirKey d k = d </> hashDirMixed k </> f </> f
+{- Where to store a given Key in the Directory.
+ -
+ - There are two possible locations to try; this had to be done because
+ - on Linux, vfat filesystem mounted with shortname=mixed have a
+ - variant of case insensativity that causes miserable failure when
+ - hashDirMixed produces eg, "xx" and "XX". The first directory to be
+ - created wins the namespace, and the second one cannot then be created.
+ - But unlike behavior with shortname=lower, "XX/foo" won't look in
+ - "xx/foo".
+ -}
+locations :: FilePath -> Key -> [FilePath]
+locations d k = [using hashDirMixed, using hashDirLower]
where
+ using h = d </> h k </> f </> f
f = keyFile k
+withCheckedFile :: (FilePath -> IO Bool) -> FilePath -> Key -> (FilePath -> IO Bool) -> IO Bool
+withCheckedFile _ [] _ _ = return False
+withCheckedFile check d k a = go $ locations d k
+ where
+ go [] = return False
+ go (f:fs) = do
+ use <- check f
+ if use
+ then a f
+ else go fs
+
+withStoredFile :: FilePath -> Key -> (FilePath -> IO Bool) -> IO Bool
+withStoredFile = withCheckedFile doesFileExist
+
store :: FilePath -> Key -> Annex Bool
store d k = do
src <- fromRepo $ gitAnnexLocation k
- let dest = dirKey d k
- liftIO $ catchBoolIO $ storeHelper dest $ copyFileExternal src dest
+ liftIO $ catchBoolIO $ storeHelper d k $ copyFileExternal src
storeEncrypted :: FilePath -> (Cipher, Key) -> Key -> Annex Bool
storeEncrypted d (cipher, enck) k = do
src <- fromRepo $ gitAnnexLocation k
- let dest = dirKey d enck
- liftIO $ catchBoolIO $ storeHelper dest $ encrypt src dest
+ liftIO $ catchBoolIO $ storeHelper d enck $ encrypt src
where
encrypt src dest = do
withEncryptedContent cipher (L.readFile src) $ L.writeFile dest
return True
-storeHelper :: FilePath -> IO Bool -> IO Bool
-storeHelper dest a = do
- let dir = parentDir dest
- createDirectoryIfMissing True dir
- allowWrite dir
- ok <- a
- when ok $ do
- preventWrite dest
- preventWrite dir
- return ok
+storeHelper :: FilePath -> Key -> (FilePath -> IO Bool) -> IO Bool
+storeHelper d key a = withCheckedFile check d key go
+ where
+ check dest = isJust <$> mkdir (parentDir dest)
+ mkdir = catchMaybeIO . createDirectoryIfMissing True
+ go dest = do
+ let dir = parentDir dest
+ allowWrite dir
+ ok <- a dest
+ when ok $ do
+ preventWrite dest
+ preventWrite dir
+ return ok
retrieve :: FilePath -> Key -> FilePath -> Annex Bool
-retrieve d k f = liftIO $ copyFileExternal (dirKey d k) f
+retrieve d k f = liftIO $ withStoredFile d k $ \file -> copyFileExternal file f
retrieveEncrypted :: FilePath -> (Cipher, Key) -> FilePath -> Annex Bool
retrieveEncrypted d (cipher, enck) f =
- liftIO $ catchBoolIO $ do
- withDecryptedContent cipher (L.readFile (dirKey d enck)) $ L.writeFile f
+ liftIO $ withStoredFile d enck $ \file -> catchBoolIO $ do
+ withDecryptedContent cipher (L.readFile file) $ L.writeFile f
return True
remove :: FilePath -> Key -> Annex Bool
-remove d k = liftIO $ catchBoolIO $ do
+remove d k = liftIO $ withStoredFile d k $ \file -> catchBoolIO $ do
+ let dir = parentDir file
allowWrite dir
removeFile file
removeDirectory dir
return True
- where
- file = dirKey d k
- dir = parentDir file
checkPresent :: FilePath -> Key -> Annex (Either String Bool)
-checkPresent d k = liftIO $ catchMsgIO $ doesFileExist (dirKey d k)
+checkPresent d k = liftIO $ catchMsgIO $ withStoredFile d k $
+ const $ return True -- withStoredFile checked that it exists
diff --git a/debian/changelog b/debian/changelog
index 185ffeceb..e043f6c9f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+git-annex (3.20111123) UNRELEASED; urgency=low
+
+ * Put a workaround in the directory special remote for strange behavior
+ with VFAT filesystems on Linux (mounted with shortname=mixed)
+
+ -- Joey Hess <joeyh@debian.org> Tue, 22 Nov 2011 17:53:42 -0400
+
git-annex (3.20111122) unstable; urgency=low
* merge: Improve commit messages to mention what was merged.
diff --git a/doc/bugs/directory_remote_and_case_sensitivity_on_FAT.mdwn b/doc/bugs/directory_remote_and_case_sensitivity_on_FAT.mdwn
index b686304e5..ae653d619 100644
--- a/doc/bugs/directory_remote_and_case_sensitivity_on_FAT.mdwn
+++ b/doc/bugs/directory_remote_and_case_sensitivity_on_FAT.mdwn
@@ -18,11 +18,28 @@ I wonder if the directory remote should use hashDirLower instead of hashDirMixed
> git-annex intentionally uses the same layout for directory and rsync
> special remotes as it does for the .git/annex directory. As far
-> as I know it works ok on case-insensative filesystems.
+> as I know it works ok on (truely) case-insensative filesystems.
>
> Based on your strace, if you `ls /media/annex/Zp`, you will see
> "No such file or directory", but if you `mkdir /media/annex/Zp` it will
> fail with "File exists". Doesn't make much sense to me.
>
-> I cannot reproduce this problem using a vfat filesystem
-> mounted using the same options you show (linux 3.0.0). --[[Joey]]
+> The (default) VFAT mount option shortname=mixed causes this behavior.
+> With shortname=lower it works ok. --[[Joey]]
+>
+>> So, the options for fixing this bug seem to be to fix Linux (which would
+>> be a good idea IMHO but I don't really want to go there), or generally
+>> convert git-annex to using lowercase for its hashing (which would be a
+>> large amount of pain to rewrite all the symlinks in every git repo),
+>> or some special hack around this problem.
+>>
+>> I've put in a workaround for the problem in the directory special
+>> remote; it will use mixed case but fall-back to lowercase as necessary.
+>>
+>> That does leave the case of a bare git repository with annexed content
+>> stored on VFAT. More special casing could fix it, but that is, I
+>> think, an unusual configuration. Leaving the bug open for that case,
+>> and for the even more unlikely configuration of a rsync special remote
+>> stored on VFAT. --[[Joey]]
+
+[[!meta title="bare git repository not supported on VFAT"]]