[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-1-0' - loolwsd/TileCache.cpp loolwsd/Util.cpp loolwsd/Util.hpp

Tor Lillqvist tml at collabora.com
Tue Oct 11 16:40:21 UTC 2016


 loolwsd/TileCache.cpp |   11 ++-------
 loolwsd/Util.cpp      |   57 ++++++++++++++++++++++++++++++++++++++++++++++++++
 loolwsd/Util.hpp      |    7 ++++++
 3 files changed, 67 insertions(+), 8 deletions(-)

New commits:
commit 8055dfc089cf860806b987f7aa2dd07dc72d6f26
Author: Tor Lillqvist <tml at collabora.com>
Date:   Tue Oct 11 19:35:16 2016 +0300

    Handle disk full situations more gracefully
    
    Introduce new API in our Util namespace to save data to a file
    safely. The data is written to a temporary file in the same directory
    and after that has succeeded, it is renamed atomicaly to the intended
    name. If any step of the saving fails, neither the temporay file or
    the intended target (if one exists before) is left behind.
    
    Unlike in the master branch, here we don't show any message to the
    users when that happens. At least not yet. Back-porting that full
    functionality was harder than expected, and I wanted to get in at
    least this fix first, to avoid zero-size cached tiles.

diff --git a/loolwsd/TileCache.cpp b/loolwsd/TileCache.cpp
index 74bafc6..fb32f0c 100644
--- a/loolwsd/TileCache.cpp
+++ b/loolwsd/TileCache.cpp
@@ -122,11 +122,8 @@ void TileCache::saveTile(int part, int width, int height, int tilePosX, int tile
 {
     const std::string fileName = _cacheDir + "/" + cacheFileName(part, width, height, tilePosX, tilePosY, tileWidth, tileHeight);
 
-    Log::trace() << "Saving cache tile: " << fileName << Log::end;
-
-    std::fstream outStream(fileName, std::ios::out);
-    outStream.write(data, size);
-    outStream.close();
+    if (Util::saveDataToFileSafely(fileName, data, size))
+        Log::trace() << "Saved cache tile: " << fileName << Log::end;
 }
 
 std::string TileCache::getTextFile(const std::string& fileName)
@@ -193,9 +190,7 @@ void TileCache::saveRendering(const std::string& name, const std::string& dir, c
 
     const std::string fileName = dirName + "/" + name;
 
-    std::fstream outStream(fileName, std::ios::out);
-    outStream.write(data, size);
-    outStream.close();
+    Util::saveDataToFileSafely(fileName, data, size);
 }
 
 std::unique_ptr<std::fstream> TileCache::lookupRendering(const std::string& name, const std::string& dir)
diff --git a/loolwsd/Util.cpp b/loolwsd/Util.cpp
index 6bc06cc..8caf37f 100644
--- a/loolwsd/Util.cpp
+++ b/loolwsd/Util.cpp
@@ -18,8 +18,10 @@
 
 #include <atomic>
 #include <cassert>
+#include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <fstream>
 #include <iostream>
 #include <iomanip>
 #include <mutex>
@@ -169,6 +171,61 @@ namespace Util
         return std::getenv("DISPLAY") != nullptr;
     }
 
+    bool saveDataToFileSafely(std::string fileName, const char *data, size_t size)
+    {
+        const auto tempFileName = fileName + ".temp";
+        std::fstream outStream(tempFileName, std::ios::out);
+
+        // If we can't create the file properly, just remove it
+        if (!outStream.good())
+        {
+            Log::error("Creating " + tempFileName + " failed, disk full?");
+            // Try removing both just in case
+            std::remove(tempFileName.c_str());
+            std::remove(fileName.c_str());
+            return false;
+        }
+        else
+        {
+            outStream.write(data, size);
+            if (!outStream.good())
+            {
+                Log::error("Writing to " + tempFileName + " failed, disk full?");
+                outStream.close();
+                std::remove(tempFileName.c_str());
+                std::remove(fileName.c_str());
+                return false;
+            }
+            else
+            {
+                outStream.close();
+                if (!outStream.good())
+                {
+                    Log::error("Closing " + tempFileName + " failed, disk full?");
+                    std::remove(tempFileName.c_str());
+                    std::remove(fileName.c_str());
+                    return false;
+                }
+                else
+                {
+                    // Everything OK, rename the file to its proper name
+                    if (std::rename(tempFileName.c_str(), fileName.c_str()) == 0)
+                    {
+                        Log::debug() << "Renaming " << tempFileName << " to " << fileName << " OK." << Log::end;
+                        return true;
+                    }
+                    else
+                    {
+                        Log::error("Renaming " + tempFileName + " to " + fileName + " failed, disk full?");
+                        std::remove(tempFileName.c_str());
+                        std::remove(fileName.c_str());
+                        return false;
+                    }
+                }
+            }
+        }
+    }
+
     bool encodeBufferToPNG(unsigned char *pixmap, int width, int height, std::vector<char>& output, LibreOfficeKitTileMode mode)
     {
 
diff --git a/loolwsd/Util.hpp b/loolwsd/Util.hpp
index 8907d9c..17142f1 100644
--- a/loolwsd/Util.hpp
+++ b/loolwsd/Util.hpp
@@ -49,6 +49,13 @@ namespace Util
 
     bool windowingAvailable();
 
+    // Save data to a file (overwriting an existing file if necessary) with checks for errors. Write
+    // to a temporary file in the same directory that is then atomically renamed to the desired name
+    // if everything goes well. In case of any error, both the destination file (if it already
+    // exists) and the temporary file (if was created, or existed already) are removed. Return true
+    // if everything succeeded.
+    bool saveDataToFileSafely(std::string fileName, const char *data, size_t size);
+
     // Sadly, older libpng headers don't use const for the pixmap pointer parameter to
     // png_write_row(), so can't use const here for pixmap.
     bool encodeBufferToPNG(unsigned char* pixmap, int width, int height,


More information about the Libreoffice-commits mailing list