瀏覽代碼

Dont write directly to files but rather write and copy a tmp file

PilzAdam 10 年之前
父節點
當前提交
d718b0b34e
共有 9 個文件被更改,包括 101 次插入69 次删除
  1. 9 8
      src/ban.cpp
  2. 18 17
      src/environment.cpp
  3. 25 0
      src/filesys.cpp
  4. 2 0
      src/filesys.h
  5. 15 13
      src/map.cpp
  6. 10 7
      src/mapgen.cpp
  7. 8 12
      src/serverlist.cpp
  8. 11 10
      src/settings.h
  9. 3 2
      src/subgame.cpp

+ 9 - 8
src/ban.cpp

@@ -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;
 }
 

+ 18 - 17
src/environment.cpp

@@ -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)

+ 25 - 0
src/filesys.cpp

@@ -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
 

+ 2 - 0
src/filesys.h

@@ -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

+ 15 - 13
src/map.cpp

@@ -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;
 }

+ 10 - 7
src/mapgen.cpp

@@ -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());
 }
 
 

+ 8 - 12
src/serverlist.cpp

@@ -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;
 }

+ 11 - 10
src/settings.h

@@ -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;
 			}
 		}
 

+ 3 - 2
src/subgame.cpp

@@ -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;
 }