Bug 540310 - Eclipse Fails to start if the mtime of the bundles.info file is zero

If the mtime of bundles.info is zero, use the ctime instead on
platforms that support it

Change-Id: I7c07f12433f38a6bbaf0b8b8dd23fa8c794f438d
Signed-off-by: Mat Booth <mat.booth@redhat.com>
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTest.java
index 9df712f..c1735df 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012,2017 Red Hat, Inc. and others.
+ * Copyright (c) 2012,2018 Red Hat, Inc. and others.
  *
  * This
  * program and the accompanying materials are made available under the terms of
@@ -16,9 +16,13 @@
  ******************************************************************************/
 package org.eclipse.equinox.p2.tests.simpleconfigurator;
 
-import java.io.*;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.attribute.FileTime;
 import java.util.Properties;
 import org.eclipse.equinox.internal.simpleconfigurator.SimpleConfiguratorImpl;
 import org.eclipse.equinox.p2.tests.AbstractProvisioningTest;
@@ -118,4 +122,36 @@
 		assertIsPropertySet(true);
 	}
 
+	/**
+	 * Sets the mtime of the given file to zero, optionally record zero to the timestamp file instead of the last modified time.
+	 */
+	protected void clearLastModified(File file, boolean storeZero) throws IOException {
+		long ctime = file.lastModified();
+		file.setLastModified(0);
+		try {
+			FileTime ft = (FileTime) Files.getAttribute(file.toPath(), "unix:ctime");
+			ctime = ft.toMillis();
+		} catch (IllegalArgumentException | IOException e) {
+			// Not applicable on non-posix platforms
+		}
+		if (storeZero) {
+			storeTimestamp(0);
+		} else {
+			storeTimestamp(ctime);
+		}
+	}
+
+	// master modified, but the mtime of the master config is set to zero --> choose master
+	public void testSharedConfigurationMasterModifiedNoMtime() throws IOException {
+		clearLastModified(masterConfguration, true);
+		assertEquals(sharedConfiguration[1], configurator.chooseConfigurationURL(relativeURL, sharedConfiguration));
+		assertIsPropertySet(true);
+	}
+
+	// master not modified, but the mtime of the master config is set to zero --> choose user
+	public void testSharedConfigurationMasterUnmodifiedNoMtime() throws IOException {
+		clearLastModified(masterConfguration, false);
+		assertEquals(sharedConfiguration[0], configurator.chooseConfigurationURL(relativeURL, sharedConfiguration));
+		assertIsPropertySet(false);
+	}
 }
diff --git a/bundles/org.eclipse.equinox.simpleconfigurator/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.simpleconfigurator/META-INF/MANIFEST.MF
index a939263..76c0ee6 100644
--- a/bundles/org.eclipse.equinox.simpleconfigurator/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.simpleconfigurator/META-INF/MANIFEST.MF
@@ -1,7 +1,7 @@
 Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-SymbolicName: org.eclipse.equinox.simpleconfigurator;singleton:=true
-Bundle-Version: 1.3.100.qualifier
+Bundle-Version: 1.3.200.qualifier
 Bundle-Name: %bundleName
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml b/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml
index da9ab47..452f3ec 100644
--- a/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml
+++ b/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml
@@ -9,6 +9,6 @@
   </parent>
   <groupId>org.eclipse.equinox</groupId>
   <artifactId>org.eclipse.equinox.simpleconfigurator</artifactId>
-  <version>1.3.100-SNAPSHOT</version>
+  <version>1.3.200-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 </project>
diff --git a/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/SimpleConfiguratorImpl.java b/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/SimpleConfiguratorImpl.java
index 665d1f5..292de22 100644
--- a/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/SimpleConfiguratorImpl.java
+++ b/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/SimpleConfiguratorImpl.java
@@ -170,7 +170,7 @@
 	public static long[] getCurrentBundlesInfoBaseTimestamp(File sharedBundlesInfo) {
 		if (!sharedBundlesInfo.exists())
 			return new long[] {NO_TIMESTAMP, NO_TIMESTAMP};
-		long lastModified = sharedBundlesInfo.lastModified();
+		long lastModified = SimpleConfiguratorUtils.getFileLastModified(sharedBundlesInfo);
 		long extLastModified = SimpleConfiguratorUtils.getExtendedTimeStamp();
 		return new long[] {lastModified, extLastModified};
 	}
diff --git a/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/utils/SimpleConfiguratorUtils.java b/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/utils/SimpleConfiguratorUtils.java
index 5ae1159..c6512e3 100644
--- a/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/utils/SimpleConfiguratorUtils.java
+++ b/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/utils/SimpleConfiguratorUtils.java
@@ -18,6 +18,7 @@
 import java.io.*;
 import java.net.*;
 import java.nio.file.Files;
+import java.nio.file.attribute.FileTime;
 import java.util.*;
 import org.eclipse.equinox.internal.simpleconfigurator.Activator;
 import org.osgi.framework.Version;
@@ -387,7 +388,7 @@
 			try {
 				ArrayList<File> infoFiles = SimpleConfiguratorUtils.getInfoFiles();
 				for (File f : infoFiles) {
-					long infoFileLastModified = f.lastModified();
+					long infoFileLastModified = getFileLastModified(f);
 					// pick latest modified always
 					if (infoFileLastModified > regularTimestamp) {
 						regularTimestamp = infoFileLastModified;
@@ -404,4 +405,19 @@
 		}
 		return regularTimestamp;
 	}
+
+	public static long getFileLastModified(File file) {
+		long lastModified = file.lastModified();
+		if (lastModified == 0) {
+			try {
+				// Note that "ctime" is different to a file's creation time (on posix
+				// platforms creation time is a synonym for last modified time)
+				FileTime ctime = (FileTime) Files.getAttribute(file.toPath(), "unix:ctime");
+				lastModified = ctime.toMillis();
+			} catch (IllegalArgumentException | IOException e) {
+				// We expect this attribute to not exist on non-posix platforms like Windows
+			}
+		}
+		return lastModified;
+	}
 }