From 10f4b70fbd27897989a8f0cecde435cd71e8a085 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauldmccarthy@gmail.com>
Date: Fri, 7 Sep 2018 12:31:14 +0100
Subject: [PATCH] RF: Don't try and save over a memory-mapped image file

---
 fsl/data/image.py | 50 ++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 43 insertions(+), 7 deletions(-)

diff --git a/fsl/data/image.py b/fsl/data/image.py
index 0a130dcaa..7af1e6960 100644
--- a/fsl/data/image.py
+++ b/fsl/data/image.py
@@ -35,8 +35,10 @@ and file names:
 
 import                      os
 import os.path           as op
+import                      shutil
 import                      string
 import                      logging
+import                      tempfile
 
 import                      six
 import                      deprecation
@@ -1165,11 +1167,41 @@ class Image(Nifti):
         log.debug('Saving {} to {}'.format(self.name, filename))
 
         # If this Image is not managing its
-        # own file object, nibabel does all
-        # of the hard work.
-        if self.__fileobj is None:
+        # own file object, and the image is not
+        # memory-mapped, nibabel does all of
+        # the hard work.
+        newnibimage = False
+        ismmap      = isinstance(self[0, 0, :10], np.memmap)
+        if self.__fileobj is None and (not ismmap):
             nib.save(self.__nibImage, filename)
 
+        # If the image is memory-mapped, we need
+        # to close and re-open the image
+        elif self.__fileobj is None:
+
+            # We save the image out to a temp file,
+            # then close the old image, move the
+            # temp file to the real destination,
+            # then re-open the file.
+            newnibimage     = True
+            tmphd, tmpfname = tempfile.mkstemp(suffix=op.splitext(filename)[1])
+            os.close(tmphd)
+
+            try:
+                nib.save(self.__nibImage, tmpfname)
+
+                self.__nibImage = None
+                self.header     = None
+
+                shutil.copy(tmpfname, filename)
+
+                self.__nibImage = nib.load(filename)
+                self.header     = self.__nibImage.header
+
+            except Exception:
+                os.remove(tmpfname)
+                raise
+
         # Otherwise we've got our own file
         # handle to an IndexedGzipFile
         else:
@@ -1190,15 +1222,19 @@ class Image(Nifti):
             # compressed data. And then be able to
             # transfer the index generated from the
             # write to a new read-only file handle.
+            newnibimage = True
             nib.save(self.__nibImage, filename)
             self.__fileobj.close()
             self.__nibImage, self.__fileobj = loadIndexedImageFile(filename)
             self.header = self.__nibImage.header
 
-            # We have to create a new ImageWrapper
-            # instance too, as we have just destroyed
-            # the nibabel image we gave to the last
-            # one.
+        # If we've created a new nibabel image,
+        # we have to create a new ImageWrapper
+        # instance too, as we have just destroyed
+        # the nibabel image we gave to the last
+        # one.
+        if newnibimage:
+
             self.__imageWrapper.deregister(self.__lName)
             self.__imageWrapper = imagewrapper.ImageWrapper(
                 self.nibImage,
-- 
GitLab