You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by jo...@apache.org on 2018/01/30 14:25:03 UTC

commons-io git commit: PR: IO-567

Repository: commons-io
Updated Branches:
  refs/heads/master 459cebc51 -> 72d00532a


PR: IO-567

Throw an IllegalArgumentException in FilenameUtils.getExtension(String)
for ADS names.


Project: http://git-wip-us.apache.org/repos/asf/commons-io/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-io/commit/72d00532
Tree: http://git-wip-us.apache.org/repos/asf/commons-io/tree/72d00532
Diff: http://git-wip-us.apache.org/repos/asf/commons-io/diff/72d00532

Branch: refs/heads/master
Commit: 72d00532aa8f0fb96371f01688ace8b9d53b17b8
Parents: 459cebc
Author: Jochen Wiedmann (jwi) <jo...@softwareag.com>
Authored: Tue Jan 30 15:24:39 2018 +0100
Committer: Jochen Wiedmann (jwi) <jo...@softwareag.com>
Committed: Tue Jan 30 15:24:39 2018 +0100

----------------------------------------------------------------------
 .../org/apache/commons/io/FilenameUtils.java    | 43 +++++++++++++++++++-
 .../apache/commons/io/NtfsAdsNameException.java | 18 ++++++++
 .../commons/io/FilenameUtilsTestCase.java       | 30 +++++++++++++-
 3 files changed, 88 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-io/blob/72d00532/src/main/java/org/apache/commons/io/FilenameUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/io/FilenameUtils.java b/src/main/java/org/apache/commons/io/FilenameUtils.java
index 9cddebb..382df41 100644
--- a/src/main/java/org/apache/commons/io/FilenameUtils.java
+++ b/src/main/java/org/apache/commons/io/FilenameUtils.java
@@ -716,15 +716,28 @@ public class FilenameUtils {
      * <p>
      * The output will be the same irrespective of the machine that the code is running on.
      * </p>
+     * <b>Note:</b> This method used to have a hidden problem for names like "foo.exe:bar.txt".
+     * In this case, the name wouldn't be the name of a file, but the identifier of an
+     * alternate data stream (bar.txt) on the file foo.exe. The method used to return
+     * ".txt" here, which would be misleading. Commons IO 2.7, and later versions, are throwing
+     * at {@link NtfsAdsNameException} for names like this.
      *
      * @param filename
      *            the filename to find the last extension separator in, null returns -1
      * @return the index of the last extension separator character, or -1 if there is no such character
+     * @throws NtfsAdsNameException The filename argument 
      */
-    public static int indexOfExtension(final String filename) {
+    public static int indexOfExtension(final String filename) throws NtfsAdsNameException {
         if (filename == null) {
             return NOT_FOUND;
         }
+        if (isSystemWindows()) {
+        	// Special handling for NTFS ADS: Don't accept colon in the filename.
+        	final int offset = filename.indexOf(':', getAdsCriticalOffset(filename));
+        	if (offset != -1) {
+        		throw new NtfsAdsNameException("NTFS ADS separator (':') in filename is forbidden.");
+        	}
+        }
         final int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
         final int lastSeparator = indexOfLastSeparator(filename);
         return lastSeparator > extensionPos ? NOT_FOUND : extensionPos;
@@ -1027,12 +1040,19 @@ public class FilenameUtils {
      * </pre>
      * <p>
      * The output will be the same irrespective of the machine that the code is running on.
+     * <b>Note:</b> This method used to have a hidden problem for names like "foo.exe:bar.txt".
+     * In this case, the name wouldn't be the name of a file, but the identifier of an
+     * alternate data stream (bar.txt) on the file foo.exe. The method used to return
+     * ".txt" here, which would be misleading. Commons IO 2.7, and later versions, are throwing
+     * at {@link NtfsAdsNameException} for names like this.
      *
      * @param filename the filename to retrieve the extension of.
      * @return the extension of the file or an empty string if none exists or {@code null}
      * if the filename is {@code null}.
+     * @throws NtfsAdsNameException <b>Windows only:/b> The filename parameter is, in fact,
+     * the identifier of an Alternate Data Stream, for example "foo.exe:bar.txt".
      */
-    public static String getExtension(final String filename) {
+    public static String getExtension(final String filename) throws NtfsAdsNameException {
         if (filename == null) {
             return null;
         }
@@ -1044,6 +1064,25 @@ public class FilenameUtils {
         }
     }
 
+    private static int getAdsCriticalOffset(String filename) {
+    	// Step 1: Remove leading path segments.
+    	int offset1 = filename.lastIndexOf(SYSTEM_SEPARATOR);
+    	int offset2 = filename.lastIndexOf(OTHER_SEPARATOR);
+    	if (offset1 == -1) {
+    		if (offset2 == -1) {
+    			return 0;
+    		} else {
+    			return offset2 + 1;
+    		}
+    	} else {
+    		if (offset2 == -1) {
+    			return offset1+1;
+    		} else {
+    			return Math.max(offset1, offset2)+1;
+    		}
+    	}
+    }
+    
     //-----------------------------------------------------------------------
     /**
      * Removes the extension from a filename.

http://git-wip-us.apache.org/repos/asf/commons-io/blob/72d00532/src/main/java/org/apache/commons/io/NtfsAdsNameException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/io/NtfsAdsNameException.java b/src/main/java/org/apache/commons/io/NtfsAdsNameException.java
new file mode 100644
index 0000000..2ca9ab1
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/NtfsAdsNameException.java
@@ -0,0 +1,18 @@
+package org.apache.commons.io;
+
+
+/**
+ * This exception is thrown, if an NTFS ADS name is passed to certain methods,
+ * where it might affect the result. For example, if you pass a name like
+ * "foo.exe:bar.txt" to {@link FilenameUtils#getExtension(String)}, then it
+ * might return ".txt", which would be misleading, because the actual extension
+ * would be ".txt".
+ */
+public class NtfsAdsNameException extends IllegalArgumentException {
+
+	private static final long serialVersionUID = -9158109384797441214L;
+
+	public NtfsAdsNameException(String pMessage) {
+		super(pMessage);
+	}
+}

http://git-wip-us.apache.org/repos/asf/commons-io/blob/72d00532/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java b/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java
index 234c25e..ea38b50 100644
--- a/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java
+++ b/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java
@@ -35,6 +35,8 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
+import org.junit.Assert;
+
 /**
  * This is used to test FilenameUtils for correctness.
  *
@@ -580,6 +582,20 @@ public class FilenameUtilsTestCase {
         assertEquals(-1, FilenameUtils.indexOfExtension("a\\b\\c"));
         assertEquals(-1, FilenameUtils.indexOfExtension("a/b.notextension/c"));
         assertEquals(-1, FilenameUtils.indexOfExtension("a\\b.notextension\\c"));
+
+        if (FilenameUtils.isSystemWindows()) {
+            // Special case handling for NTFS ADS names
+        	try {
+        		FilenameUtils.indexOfExtension("foo.exe:bar.txt");
+        		throw new AssertionError("Expected Exception");
+        	} catch (IllegalArgumentException e) {
+        		assertEquals("NTFS ADS separator (':') in filename is forbidden.", e.getMessage());
+        	}
+        } else {
+        	// Upwards compatibility on other systems
+        	assertEquals(11, FilenameUtils.indexOfExtension("foo.exe:bar.txt"));
+        }
+
     }
 
     //-----------------------------------------------------------------------
@@ -862,6 +878,19 @@ public class FilenameUtilsTestCase {
         assertEquals("", FilenameUtils.getExtension("a\\b\\c"));
         assertEquals("", FilenameUtils.getExtension("C:\\temp\\foo.bar\\README"));
         assertEquals("ext", FilenameUtils.getExtension("../filename.ext"));
+
+        if (FilenameUtils.isSystemWindows()) {
+            // Special case handling for NTFS ADS names
+        	try {
+        		FilenameUtils.getExtension("foo.exe:bar.txt");
+        		throw new AssertionError("Expected Exception");
+        	} catch (IllegalArgumentException e) {
+        		assertEquals("NTFS ADS separator (':') in filename is forbidden.", e.getMessage());
+        	}
+        } else {
+        	// Upwards compatibility:
+        	assertEquals("txt", FilenameUtils.getExtension("foo.exe:bar.txt"));
+        }
     }
 
     @Test
@@ -1082,5 +1111,4 @@ public class FilenameUtilsTestCase {
         assertFalse(FilenameUtils.isExtension("a.b\\file.txt", new ArrayList<>(Arrays.asList(new String[]{"TXT"}))));
         assertFalse(FilenameUtils.isExtension("a.b\\file.txt", new ArrayList<>(Arrays.asList(new String[]{"TXT", "RTF"}))));
     }
-
 }