Dont write directly to files but rather write and copy a tmp file
authorPilzAdam <pilzadam@minetest.net>
Tue, 13 Aug 2013 17:15:06 +0000 (19:15 +0200)
committerPilzAdam <pilzadam@minetest.net>
Tue, 13 Aug 2013 20:05:45 +0000 (22:05 +0200)
src/ban.cpp
src/environment.cpp
src/filesys.cpp
src/filesys.h
src/map.cpp
src/mapgen.cpp
src/serverlist.cpp
src/settings.h
src/subgame.cpp

index 75bae746f9455131705a27763d382014ca6a7a7e..f9d32b605c2b6a20766f227791cb9ccb717bb10d 100644 (file)
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <set>
 #include "strfnd.h"
 #include "log.h"
+#include "filesys.h"
 
 BanManager::BanManager(const std::string &banfilepath):
                m_banfilepath(banfilepath),
@@ -76,20 +77,20 @@ void BanManager::save()
 {
        JMutexAutoLock lock(m_mutex);
        infostream<<"BanManager: saving to "<<m_banfilepath<<std::endl;
-       std::ofstream os(m_banfilepath.c_str(), std::ios::binary);
-       
-       if(os.good() == false)
-       {
-               infostream<<"BanManager: failed saving to "<<m_banfilepath<<std::endl;
-               throw SerializationError("BanManager::load(): Couldn't open file");
-       }
+       std::ostringstream ss(std::ios_base::binary);
 
        for(std::map<std::string, std::string>::iterator
                        i = m_ips.begin();
                        i != m_ips.end(); i++)
        {
-               os<<i->first<<"|"<<i->second<<"\n";
+               ss << i->first << "|" << i->second << "\n";
        }
+
+       if(!fs::safeWriteToFile(m_banfilepath, ss.str())) {
+               infostream<<"BanManager: failed saving to "<<m_banfilepath<<std::endl;
+               throw SerializationError("BanManager::load(): Couldn't write file");
+       }
+
        m_modified = false;
 }
 
index 35983aaaf30b8e216fa5cc4383d6ff40b28b4be0..eacc2a008cad11e31b1766f9e3c15e8a8513a206 100644 (file)
@@ -437,13 +437,13 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
                if(player->checkModified())
                {
                        // Open file and serialize
-                       std::ofstream os(path.c_str(), std::ios_base::binary);
-                       if(os.good() == false)
+                       std::ostringstream ss(std::ios_base::binary);
+                       player->serialize(ss);
+                       if(!fs::safeWriteToFile(path, ss.str()))
                        {
-                               infostream<<"Failed to overwrite "<<path<<std::endl;
+                               infostream<<"Failed to write "<<path<<std::endl;
                                continue;
                        }
-                       player->serialize(os);
                        saved_players.insert(player);
                } else {
                        saved_players.insert(player);
@@ -493,13 +493,13 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
                        /*infostream<<"Saving player "<<player->getName()<<" to "
                                        <<path<<std::endl;*/
                        // Open file and serialize
-                       std::ofstream os(path.c_str(), std::ios_base::binary);
-                       if(os.good() == false)
+                       std::ostringstream ss(std::ios_base::binary);
+                       player->serialize(ss);
+                       if(!fs::safeWriteToFile(path, ss.str()))
                        {
-                               infostream<<"Failed to overwrite "<<path<<std::endl;
+                               infostream<<"Failed to write "<<path<<std::endl;
                                continue;
                        }
-                       player->serialize(os);
                        saved_players.insert(player);
                }
        }
@@ -581,19 +581,20 @@ void ServerEnvironment::saveMeta(const std::string &savedir)
        std::string path = savedir + "/env_meta.txt";
 
        // Open file and serialize
-       std::ofstream os(path.c_str(), std::ios_base::binary);
-       if(os.good() == false)
-       {
-               infostream<<"ServerEnvironment::saveMeta(): Failed to open "
-                               <<path<<std::endl;
-               throw SerializationError("Couldn't save env meta");
-       }
+       std::ostringstream ss(std::ios_base::binary);
 
        Settings args;
        args.setU64("game_time", m_game_time);
        args.setU64("time_of_day", getTimeOfDay());
-       args.writeLines(os);
-       os<<"EnvArgsEnd\n";
+       args.writeLines(ss);
+       ss<<"EnvArgsEnd\n";
+
+       if(!fs::safeWriteToFile(path, ss.str()))
+       {
+               infostream<<"ServerEnvironment::saveMeta(): Failed to write "
+                               <<path<<std::endl;
+               throw SerializationError("Couldn't save env meta");
+       }
 }
 
 void ServerEnvironment::loadMeta(const std::string &savedir)
index 356d3018deff2843816f19a87e486479240efa85..a1795c8eadf9873ae77a41b9e3a7df5e88683c42 100644 (file)
@@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <sstream>
+#include <fstream>
 #include "log.h"
 
 namespace fs
@@ -684,5 +686,28 @@ std::string RemoveRelativePathComponents(std::string path)
        return path.substr(0, pos);
 }
 
+bool safeWriteToFile(const std::string &path, const std::string &content)
+{
+       std::string tmp_file = path + ".~mt";
+
+       // Write to a tmp file
+       std::ofstream os(tmp_file.c_str(), std::ios::binary);
+       if (!os.good())
+               return false;
+       os << content;
+       os.flush();
+       os.close();
+       if (os.fail())
+               return false;
+
+       // Copy file
+#ifdef _WIN32
+       remove(path.c_str());
+       return (rename(tmp_file.c_str(), path.c_str()) == 0);
+#else
+       return (rename(tmp_file.c_str(), path.c_str()) == 0);
+#endif
+}
+
 } // namespace fs
 
index d0bf400c78921ca44d8891964a8d2e32dfd45c5d..1b3659afee947d8f2ecd3ff78a71fe36de5bdf86 100644 (file)
@@ -98,6 +98,8 @@ std::string RemoveLastPathComponent(std::string path,
 // this does not resolve symlinks and check for existence of directories.
 std::string RemoveRelativePathComponents(std::string path);
 
+bool safeWriteToFile(const std::string &path, const std::string &content);
+
 }//fs
 
 #endif
index da20b5b0e1e9993d85ca6002b0a4b4dc3e7f3aa4..331aa48d943541e5c2856ed05480387bfe134201 100644 (file)
@@ -3490,20 +3490,21 @@ void ServerMap::saveMapMeta()
        createDirs(m_savedir);
 
        std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
-       std::ofstream os(fullpath.c_str(), std::ios_base::binary);
-       if(os.good() == false)
-       {
-               infostream<<"ERROR: ServerMap::saveMapMeta(): "
-                               <<"could not open"<<fullpath<<std::endl;
-               throw FileNotGoodException("Cannot open chunk metadata");
-       }
+       std::ostringstream ss(std::ios_base::binary);
 
        Settings params;
 
        m_emerge->setParamsToSettings(&params);
-       params.writeLines(os);
+       params.writeLines(ss);
 
-       os<<"[end_of_params]\n";
+       ss<<"[end_of_params]\n";
+
+       if(!fs::safeWriteToFile(fullpath, ss.str()))
+       {
+               infostream<<"ERROR: ServerMap::saveMapMeta(): "
+                               <<"could not write "<<fullpath<<std::endl;
+               throw FileNotGoodException("Cannot save chunk metadata");
+       }
 
        m_map_metadata_changed = false;
 }
@@ -3574,11 +3575,12 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector)
        createDirs(dir);
 
        std::string fullpath = dir + DIR_DELIM + "meta";
-       std::ofstream o(fullpath.c_str(), std::ios_base::binary);
-       if(o.good() == false)
-               throw FileNotGoodException("Cannot open sector metafile");
+       std::ostringstream ss(std::ios_base::binary);
+
+       sector->serialize(ss, version);
 
-       sector->serialize(o, version);
+       if(!fs::safeWriteToFile(fullpath, ss.str()))
+               throw FileNotGoodException("Cannot write sector metafile");
 
        sector->differs_from_disk = false;
 }
index 97d672b01df1478d371371eb725320ea3c23a90a..be65694aa27a28f6473e7b55507b8ed4e5d2fb33 100644 (file)
@@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapgen_v6.h"
 #include "mapgen_v7.h"
 #include "util/serialize.h"
+#include "filesys.h"
 
 FlagDesc flagdesc_mapgen[] = {
        {"trees",          MG_TREES},
@@ -756,24 +757,26 @@ bool DecoSchematic::loadSchematicFile() {
        2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
 */
 void DecoSchematic::saveSchematicFile(INodeDefManager *ndef) {
-       std::ofstream os(filename.c_str(), std::ios_base::binary);
+       std::ostringstream ss(std::ios_base::binary);
 
-       writeU32(os, MTSCHEM_FILE_SIGNATURE); // signature
-       writeU16(os, 2);      // version
-       writeV3S16(os, size); // schematic size
+       writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature
+       writeU16(ss, 2);      // version
+       writeV3S16(ss, size); // schematic size
        
        std::vector<content_t> usednodes;
        int nodecount = size.X * size.Y * size.Z;
        build_nnlist_and_update_ids(schematic, nodecount, &usednodes);
        
        u16 numids = usednodes.size();
-       writeU16(os, numids); // name count
+       writeU16(ss, numids); // name count
        for (int i = 0; i != numids; i++)
-               os << serializeString(ndef->get(usednodes[i]).name); // node names
+               ss << serializeString(ndef->get(usednodes[i]).name); // node names
                
        // compressed bulk node data
-       MapNode::serializeBulk(os, SER_FMT_VER_HIGHEST_WRITE, schematic,
+       MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schematic,
                                nodecount, 2, 2, true);
+
+       fs::safeWriteToFile(filename, ss.str());
 }
 
 
index bc09f6c0ad44f5b33cf40b6156c81de38316e5b5..4db5f3ecd5239d0a35fc546228f8ee14a08fbe03 100644 (file)
@@ -105,13 +105,11 @@ bool deleteEntry (ServerListSpec server)
        }
 
        std::string path = ServerList::getFilePath();
-       std::ofstream stream (path.c_str());
-       if (stream.is_open())
-       {
-               stream<<ServerList::serialize(serverlist);
-               return true;
-       }
-       return false;
+       std::ostringstream ss(std::ios_base::binary);
+       ss << ServerList::serialize(serverlist);
+       if (!fs::safeWriteToFile(path, ss.str()))
+               return false;
+       return true;
 }
 
 /*
@@ -128,11 +126,9 @@ bool insert (ServerListSpec server)
        serverlist.insert(serverlist.begin(), server);
 
        std::string path = ServerList::getFilePath();
-       std::ofstream stream (path.c_str());
-       if (stream.is_open())
-       {
-               stream<<ServerList::serialize(serverlist);
-       }
+       std::ostringstream ss(std::ios_base::binary);
+       ss << ServerList::serialize(serverlist);
+       fs::safeWriteToFile(path, ss.str());
 
        return false;
 }
index e7b49b6d7798497cc3d25f8bda08a596abe4acf9..a9e0faa40a59898fc793f0c327bcf54ef75f8d5b 100644 (file)
@@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <list>
 #include <map>
 #include <set>
+#include "filesys.h"
 
 enum ValueType
 {
@@ -308,14 +309,7 @@ public:
 
                // Write stuff back
                {
-                       std::ofstream os(filename);
-                       if(os.good() == false)
-                       {
-                               errorstream<<"Error opening configuration file"
-                                               " for writing: \""
-                                               <<filename<<"\""<<std::endl;
-                               return false;
-                       }
+                       std::ostringstream ss(std::ios_base::binary);
 
                        /*
                                Write updated stuff
@@ -324,7 +318,7 @@ public:
                                        i = objects.begin();
                                        i != objects.end(); ++i)
                        {
-                               os<<(*i);
+                               ss<<(*i);
                        }
 
                        /*
@@ -340,7 +334,14 @@ public:
                                std::string value = i->second;
                                infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
                                                <<std::endl;
-                               os<<name<<" = "<<value<<"\n";
+                               ss<<name<<" = "<<value<<"\n";
+                       }
+
+                       if(!fs::safeWriteToFile(filename, ss.str()))
+                       {
+                               errorstream<<"Error writing configuration file: \""
+                                               <<filename<<"\""<<std::endl;
+                               return false;
                        }
                }
 
index 7fee3899d4ace353f7555735034f25390475c215..806d6593451adfd1629fd2602f661ba319d43b1f 100644 (file)
@@ -241,8 +241,9 @@ bool initializeWorld(const std::string &path, const std::string &gameid)
        if(!fs::PathExists(worldmt_path)){
                infostream<<"Creating world.mt ("<<worldmt_path<<")"<<std::endl;
                fs::CreateAllDirs(path);
-               std::ofstream of(worldmt_path.c_str(), std::ios::binary);
-               of<<"gameid = "<<gameid<<"\n";
+               std::ostringstream ss(std::ios_base::binary);
+               ss<<"gameid = "<<gameid<<"\n";
+               fs::safeWriteToFile(worldmt_path, ss.str());
        }
        return true;
 }