@@ -716,15 +716,28 @@ public static int indexOfLastSeparator(final String filename) {
716716 * <p>
717717 * The output will be the same irrespective of the machine that the code is running on.
718718 * </p>
719+ * <b>Note:</b> This method used to have a hidden problem for names like "foo.exe:bar.txt".
720+ * In this case, the name wouldn't be the name of a file, but the identifier of an
721+ * alternate data stream (bar.txt) on the file foo.exe. The method used to return
722+ * ".txt" here, which would be misleading. Commons IO 2.7, and later versions, are throwing
723+ * at {@link NtfsAdsNameException} for names like this.
719724 *
720725 * @param filename
721726 * the filename to find the last extension separator in, null returns -1
722727 * @return the index of the last extension separator character, or -1 if there is no such character
728+ * @throws NtfsAdsNameException The filename argument
723729 */
724- public static int indexOfExtension (final String filename ) {
730+ public static int indexOfExtension (final String filename ) throws NtfsAdsNameException {
725731 if (filename == null ) {
726732 return NOT_FOUND ;
727733 }
734+ if (isSystemWindows ()) {
735+ // Special handling for NTFS ADS: Don't accept colon in the filename.
736+ final int offset = filename .indexOf (':' , getAdsCriticalOffset (filename ));
737+ if (offset != -1 ) {
738+ throw new NtfsAdsNameException ("NTFS ADS separator (':') in filename is forbidden." );
739+ }
740+ }
728741 final int extensionPos = filename .lastIndexOf (EXTENSION_SEPARATOR );
729742 final int lastSeparator = indexOfLastSeparator (filename );
730743 return lastSeparator > extensionPos ? NOT_FOUND : extensionPos ;
@@ -1027,12 +1040,19 @@ public static String getBaseName(final String filename) {
10271040 * </pre>
10281041 * <p>
10291042 * The output will be the same irrespective of the machine that the code is running on.
1043+ * <b>Note:</b> This method used to have a hidden problem for names like "foo.exe:bar.txt".
1044+ * In this case, the name wouldn't be the name of a file, but the identifier of an
1045+ * alternate data stream (bar.txt) on the file foo.exe. The method used to return
1046+ * ".txt" here, which would be misleading. Commons IO 2.7, and later versions, are throwing
1047+ * at {@link NtfsAdsNameException} for names like this.
10301048 *
10311049 * @param filename the filename to retrieve the extension of.
10321050 * @return the extension of the file or an empty string if none exists or {@code null}
10331051 * if the filename is {@code null}.
1052+ * @throws NtfsAdsNameException <b>Windows only:/b> The filename parameter is, in fact,
1053+ * the identifier of an Alternate Data Stream, for example "foo.exe:bar.txt".
10341054 */
1035- public static String getExtension (final String filename ) {
1055+ public static String getExtension (final String filename ) throws NtfsAdsNameException {
10361056 if (filename == null ) {
10371057 return null ;
10381058 }
@@ -1044,6 +1064,25 @@ public static String getExtension(final String filename) {
10441064 }
10451065 }
10461066
1067+ private static int getAdsCriticalOffset (String filename ) {
1068+ // Step 1: Remove leading path segments.
1069+ int offset1 = filename .lastIndexOf (SYSTEM_SEPARATOR );
1070+ int offset2 = filename .lastIndexOf (OTHER_SEPARATOR );
1071+ if (offset1 == -1 ) {
1072+ if (offset2 == -1 ) {
1073+ return 0 ;
1074+ } else {
1075+ return offset2 + 1 ;
1076+ }
1077+ } else {
1078+ if (offset2 == -1 ) {
1079+ return offset1 +1 ;
1080+ } else {
1081+ return Math .max (offset1 , offset2 )+1 ;
1082+ }
1083+ }
1084+ }
1085+
10471086 //-----------------------------------------------------------------------
10481087 /**
10491088 * Removes the extension from a filename.
0 commit comments