From be0cb84f972cda56d69dd1c9ddcb7334755b663a Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Tue, 14 Oct 2014 16:47:41 +0000
Subject: [PATCH] Util: Use write-sync-close-rename for config file writing

---
 core/java/src/net/i2p/data/DataHelper.java | 12 +++++++++++-
 core/java/src/net/i2p/util/FileUtil.java   | 17 ++++++++++++-----
 2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java
index ac51f8128c..d3d49dd2e7 100644
--- a/core/java/src/net/i2p/data/DataHelper.java
+++ b/core/java/src/net/i2p/data/DataHelper.java
@@ -18,6 +18,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -40,6 +41,7 @@ import java.util.zip.Deflater;
 
 import net.i2p.I2PAppContext;
 import net.i2p.util.ByteCache;
+import net.i2p.util.FileUtil;
 import net.i2p.util.OrderedProperties;
 import net.i2p.util.ReusableGZIPInputStream;
 import net.i2p.util.ReusableGZIPOutputStream;
@@ -465,8 +467,10 @@ public class DataHelper {
     public static void storeProps(Properties props, File file) throws IOException {
         PrintWriter out = null;
         IllegalArgumentException iae = null;
+        File tmpFile = new File(file.getPath() + ".tmp");
         try {
-            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
+            FileOutputStream fos = new SecureFileOutputStream(tmpFile);
+            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fos, "UTF-8")));
             out.println("# NOTE: This I2P config file must use UTF-8 encoding");
             for (Map.Entry<Object, Object> entry : props.entrySet()) {
                 String name = (String) entry.getKey();
@@ -491,6 +495,12 @@ public class DataHelper {
                 }
                 out.println(name + "=" + val);
             }
+            out.flush();
+            fos.getFD().sync();
+            out.close();
+            out = null;
+            if (!FileUtil.rename(tmpFile, file))
+                throw new IOException("Failed rename from " + tmpFile + " to " + file);
         } finally {
             if (out != null) out.close();
         }
diff --git a/core/java/src/net/i2p/util/FileUtil.java b/core/java/src/net/i2p/util/FileUtil.java
index 02024d02e0..535e3deee5 100644
--- a/core/java/src/net/i2p/util/FileUtil.java
+++ b/core/java/src/net/i2p/util/FileUtil.java
@@ -480,11 +480,12 @@ public class FileUtil {
         boolean success = false;
         boolean isWindows = SystemVersion.isWindows();
         // overwrite fails on windows
-        if (!isWindows)
+        boolean exists = to.exists();
+        if (!isWindows || !exists)
             success = from.renameTo(to);
         if (!success) {
-            to.delete();
-            success = from.renameTo(to);
+            if (exists && to.delete())
+                success = from.renameTo(to);
             if (!success) {
                 // hard way
                 success = copy(from, to, true, true);
@@ -496,12 +497,12 @@ public class FileUtil {
     }
 
     /**
-     * Usage: FileUtil (delete path | copy source dest | unzip path.zip)
+     * Usage: FileUtil (delete path | copy source dest | rename from to | unzip path.zip)
      *
      */
     public static void main(String args[]) {
         if ( (args == null) || (args.length < 2) ) {
-            System.err.println("Usage: delete path | copy source dest | unzip path.zip");
+            System.err.println("Usage: delete path | copy source dest | rename from to | unzip path.zip");
             //testRmdir();
         } else if ("delete".equals(args[0])) {
             boolean deleted = FileUtil.rmdir(args[1], false);
@@ -523,6 +524,12 @@ public class FileUtil {
                 System.err.println("Unzipped [" + args[1] + "] to [" + to + "]");
             else
                 System.err.println("Error unzipping [" + args[1] + "] to [" + to + "]");
+        } else if ("rename".equals(args[0])) {
+            boolean success = rename(new File(args[1]), new File(args[2]));
+            if (!success) 
+                System.err.println("Error renaming [" + args[1] + "] to [" + args[2] + "]");
+        } else {
+            System.err.println("Usage: delete path | copy source dest | rename from to | unzip path.zip");
         }
     }
     
-- 
GitLab