changeset 30539:29b35dac3b1f

fsmonitor: be robust in the face of bad state fsmonitor could write out bad state if interrupted part way through, and would then crash when it tried to read it back in. Make both sides of the operation more robust - reading state should fail cleanly, and we can use atomictemp to write out cleanly as the file is small. Between the two, we shouldn't crash with an IndexError any more.
author Simon Farnsworth <simonfar@fb.com>
date Fri, 25 Nov 2016 07:30:46 -0800
parents c2154979409d
children d955cebd8d6a
files hgext/fsmonitor/state.py
diffstat 1 files changed, 8 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/fsmonitor/state.py	Wed Nov 23 23:47:38 2016 +0100
+++ b/hgext/fsmonitor/state.py	Fri Nov 25 07:30:46 2016 -0800
@@ -59,6 +59,12 @@
             state = file.read().split('\0')
             # state = hostname\0clock\0ignorehash\0 + list of files, each
             # followed by a \0
+            if len(state) < 3:
+                self._ui.log(
+                    'fsmonitor', 'fsmonitor: state file truncated (expected '
+                    '3 chunks, found %d), nuking state\n', len(state))
+                self.invalidate()
+                return None, None, None
             diskhostname = state[0]
             hostname = socket.gethostname()
             if diskhostname != hostname:
@@ -85,12 +91,12 @@
             return
 
         try:
-            file = self._opener('fsmonitor.state', 'wb')
+            file = self._opener('fsmonitor.state', 'wb', atomictemp=True)
         except (IOError, OSError):
             self._ui.warn(_("warning: unable to write out fsmonitor state\n"))
             return
 
-        try:
+        with file:
             file.write(struct.pack(_versionformat, _version))
             file.write(socket.gethostname() + '\0')
             file.write(clock + '\0')
@@ -98,8 +104,6 @@
             if notefiles:
                 file.write('\0'.join(notefiles))
                 file.write('\0')
-        finally:
-            file.close()
 
     def invalidate(self):
         try: