finish fixing windows timezone madness

Rather than calculating the TSDelta once, and caching it, this now
reads the inode sential file's InodeCache file once, and then each time a
new InodeCache is generated, looks at the sentinal file to get the current
delta.

This way, if the time zone changes while git-annex is running, it will
adapt.

This adds some inneffiency, but only on Windows, and only 1 stat per new
file added. The worst innefficiency is that `git annex status` and
`git annex sync` will now (on Windows) stat the inode sentinal file once per
file in the repo.

It would be more efficient to use getCurrentTimeZone, rather than needing
to stat the sentinal file. This should be easy to do, once the time
package gets my bugfix patch.

This commit was sponsored by Jürgen Lüters.
This commit is contained in:
Joey Hess 2014-06-12 13:43:16 -04:00
parent e4d7e2ebde
commit 4fe2e53f5b
6 changed files with 39 additions and 25 deletions

View file

@ -175,9 +175,9 @@ sameInodeCache file old = go =<< withTSDelta (liftIO . genInodeCache file)
{- Checks if a FileStatus matches the recorded InodeCache of a file. -} {- Checks if a FileStatus matches the recorded InodeCache of a file. -}
sameFileStatus :: Key -> FileStatus -> Annex Bool sameFileStatus :: Key -> FileStatus -> Annex Bool
sameFileStatus key status = withTSDelta $ \delta -> do sameFileStatus key status = do
old <- recordedInodeCache key old <- recordedInodeCache key
let curr = toInodeCache delta status curr <- withTSDelta $ \delta -> liftIO $ toInodeCache delta status
case (old, curr) of case (old, curr) of
(_, Just c) -> elemInodeCaches c old (_, Just c) -> elemInodeCaches c old
([], Nothing) -> return True ([], Nothing) -> return True

View file

@ -56,8 +56,9 @@ stageDirect = do
go (file, Just sha, Just mode) = withTSDelta $ \delta -> do go (file, Just sha, Just mode) = withTSDelta $ \delta -> do
shakey <- catKey sha mode shakey <- catKey sha mode
mstat <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus file mstat <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus file
mcache <- liftIO $ maybe (pure Nothing) (toInodeCache delta) mstat
filekey <- isAnnexLink file filekey <- isAnnexLink file
case (shakey, filekey, mstat, toInodeCache delta =<< mstat) of case (shakey, filekey, mstat, mcache) of
(_, Just key, _, _) (_, Just key, _, _)
| shakey == filekey -> noop | shakey == filekey -> noop
{- A changed symlink. -} {- A changed symlink. -}

View file

@ -155,7 +155,7 @@ ingest (Just source) = withTSDelta $ \delta -> do
backend <- chooseBackend $ keyFilename source backend <- chooseBackend $ keyFilename source
k <- genKey source backend k <- genKey source backend
ms <- liftIO $ catchMaybeIO $ getFileStatus $ contentLocation source ms <- liftIO $ catchMaybeIO $ getFileStatus $ contentLocation source
let mcache = toInodeCache delta =<< ms mcache <- maybe (pure Nothing) (liftIO . toInodeCache delta) ms
case (mcache, inodeCache source) of case (mcache, inodeCache source) of
(_, Nothing) -> go k mcache ms (_, Nothing) -> go k mcache ms
(Just newc, Just c) | compareStrong c newc -> go k mcache ms (Just newc, Just c) | compareStrong c newc -> go k mcache ms

View file

@ -107,15 +107,17 @@ readInodeCache s = case words s of
genInodeCache :: FilePath -> TSDelta -> IO (Maybe InodeCache) genInodeCache :: FilePath -> TSDelta -> IO (Maybe InodeCache)
genInodeCache f delta = catchDefaultIO Nothing $ genInodeCache f delta = catchDefaultIO Nothing $
toInodeCache delta <$> getFileStatus f toInodeCache delta =<< getFileStatus f
toInodeCache :: TSDelta -> FileStatus -> Maybe InodeCache toInodeCache :: TSDelta -> FileStatus -> IO (Maybe InodeCache)
toInodeCache (TSDelta delta) s toInodeCache (TSDelta getdelta) s
| isRegularFile s = Just $ InodeCache $ InodeCachePrim | isRegularFile s = do
delta <- getdelta
return $ Just $ InodeCache $ InodeCachePrim
(fileID s) (fileID s)
(fileSize s) (fileSize s)
(modificationTime s + delta) (modificationTime s + delta)
| otherwise = Nothing | otherwise = pure Nothing
{- Some filesystem get new random inodes each time they are mounted. {- Some filesystem get new random inodes each time they are mounted.
- To detect this and other problems, a sentinal file can be created. - To detect this and other problems, a sentinal file can be created.
@ -130,12 +132,13 @@ data SentinalFile = SentinalFile
{- On Windows, the mtime of a file appears to change when the time zone is {- On Windows, the mtime of a file appears to change when the time zone is
- changed. To deal with this, a TSDelta can be used; the delta is added to - changed. To deal with this, a TSDelta can be used; the delta is added to
- the mtime when generating an InodeCache. The current delta can be found - the mtime when generating an InodeCache. The current delta can be found
- by looking at the SentinalFile. -} - by looking at the SentinalFile. Effectively, this makes all InodeCaches
newtype TSDelta = TSDelta EpochTime - use the same time zone that was in use when the sential file was
deriving (Show) - originally written. -}
newtype TSDelta = TSDelta (IO EpochTime)
noTSDelta :: TSDelta noTSDelta :: TSDelta
noTSDelta = TSDelta 0 noTSDelta = TSDelta (pure 0)
writeSentinalFile :: SentinalFile -> IO () writeSentinalFile :: SentinalFile -> IO ()
writeSentinalFile s = do writeSentinalFile s = do
@ -147,16 +150,15 @@ data SentinalStatus = SentinalStatus
{ sentinalInodesChanged :: Bool { sentinalInodesChanged :: Bool
, sentinalTSDelta :: TSDelta , sentinalTSDelta :: TSDelta
} }
deriving (Show)
{- Checks if the InodeCache of the sentinal file is the same {- Checks if the InodeCache of the sentinal file is the same
- as it was when it was originally created. - as it was when it was originally created.
- -
- On Windows, there's no change even when there is a nonzero - On Windows, time stamp differences are ignored, since they change
- TSDelta between the original and current InodeCaches. - with the timezone.
- -
- If the sential does not exist, returns a dummy value indicating - When the sential file does not exist, InodeCaches canot reliably be
- that it's apparently changed. - compared, so the assumption is that there is has been a change.
-} -}
checkSentinalFile :: SentinalFile -> IO SentinalStatus checkSentinalFile :: SentinalFile -> IO SentinalStatus
checkSentinalFile s = do checkSentinalFile s = do
@ -172,14 +174,21 @@ checkSentinalFile s = do
loadoldcache = catchDefaultIO Nothing $ loadoldcache = catchDefaultIO Nothing $
readInodeCache <$> readFile (sentinalCacheFile s) readInodeCache <$> readFile (sentinalCacheFile s)
gennewcache = genInodeCache (sentinalFile s) noTSDelta gennewcache = genInodeCache (sentinalFile s) noTSDelta
calc (InodeCache (InodeCachePrim inode1 size1 mtime1)) (InodeCache (InodeCachePrim inode2 size2 mtime2)) = calc (InodeCache (InodeCachePrim oldinode oldsize oldmtime)) (InodeCache (InodeCachePrim newinode newsize newmtime)) =
SentinalStatus (not unchanged) tsdelta SentinalStatus (not unchanged) tsdelta
where where
#ifdef mingw32_HOST_OS #ifdef mingw32_HOST_OS
unchanged = inode1 == inode2 && size1 == size2 unchanged = oldinode == newinode && oldsize == newsize
tsdelta = TSDelta (mtime1 - mtime2) tsdelta = TSDelta $ do
-- Run when generating an InodeCache,
-- to get the current delta.
mnew <- gennewcache
return $ case mnew of
Just (InodeCache (InodeCachePrim _ _ currmtime)) ->
oldmtime - currmtime
Nothing -> 0
#else #else
unchanged = inode1 == inode2 && size1 == size2 && mtime1 == mtime2 unchanged = oldinode == newinode && oldsize == newsize && oldmtime == newmtime
tsdelta = noTSDelta tsdelta = noTSDelta
#endif #endif
dummy = SentinalStatus True noTSDelta dummy = SentinalStatus True noTSDelta

1
debian/changelog vendored
View file

@ -11,6 +11,7 @@ git-annex (5.20140607) UNRELEASED; urgency=medium
Linux's caching of higher res timestamps while a FAT is mounted, caused Linux's caching of higher res timestamps while a FAT is mounted, caused
direct mode repositories on FAT to seem to have modified files after direct mode repositories on FAT to seem to have modified files after
they were unmounted and remounted. they were unmounted and remounted.
* Deal with Windows's horrible handling of time zone changes.
-- Joey Hess <joeyh@debian.org> Mon, 09 Jun 2014 14:44:09 -0400 -- Joey Hess <joeyh@debian.org> Mon, 09 Jun 2014 14:44:09 -0400

View file

@ -14,3 +14,6 @@ Unfortunately, Data.Time.LocalTime.getCurrentTimeZone doesn't seem to really
work on windows. It always returns a time zone 60 minutes from UTS in my tests, work on windows. It always returns a time zone 60 minutes from UTS in my tests,
no matter what the zone really is. I need to test this more widely and file no matter what the zone really is. I need to test this more widely and file
a GHC bug if appropriate. a GHC bug if appropriate.
> [[fixed|done]], avoiding using getCurrentTime for now, although I have a
> patch to fix it too. --[[Joey]]