This is an automated email from the ASF dual-hosted git repository. JackieTien97 pushed a commit to branch rc/2.0.10 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 34677abcc7b96d8d5f9f4347098b0a9546583edc Author: Caideyipi <[email protected]> AuthorDate: Tue Jun 9 12:28:44 2026 +0800 Fix Windows path segment validation (#17868) * Fix Windows reserved device name validation * Reject additional illegal Windows path segments * Reject empty directory path segments --- .../apache/iotdb/commons/i18n/UtilMessages.java | 1 + .../apache/iotdb/commons/i18n/UtilMessages.java | 1 + .../org/apache/iotdb/commons/utils/FileUtils.java | 3 ++ .../apache/iotdb/commons/utils/WindowsOSUtils.java | 47 +++++++++++++++++----- .../apache/iotdb/commons/utils/FileUtilsTest.java | 8 ++++ .../iotdb/commons/utils/WindowsOSUtilsTest.java | 12 ++++++ 6 files changed, 61 insertions(+), 11 deletions(-) diff --git a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/UtilMessages.java b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/UtilMessages.java index 39f4a6ce931..b13ad217f7a 100644 --- a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/UtilMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/UtilMessages.java @@ -87,6 +87,7 @@ public final class UtilMessages { "Renamed file {} to {} because it already exists in the target directory: {}"; public static final String COPIED_FILE_ALREADY_EXISTS = "Copy file {} to {} because it already exists in the target directory: {}"; + public static final String ILLEGAL_EMPTY_PATH = "The path cannot be empty. "; public static final String ILLEGAL_PATH_DOTS_OR_SEPARATORS = "The path cannot be '.', '..', './' or '.\\'. "; diff --git a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/UtilMessages.java b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/UtilMessages.java index 8fcc938dbfc..f4215f6e449 100644 --- a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/UtilMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/UtilMessages.java @@ -85,6 +85,7 @@ public final class UtilMessages { "已将文件 {} 重命名为 {},因为目标目录 {} 中已存在同名文件"; public static final String COPIED_FILE_ALREADY_EXISTS = "已将文件 {} 复制为 {},因为目标目录 {} 中已存在同名文件"; + public static final String ILLEGAL_EMPTY_PATH = "路径不能为空。 "; public static final String ILLEGAL_PATH_DOTS_OR_SEPARATORS = "路径不能为 '.'、'..'、'./' 或 '.\\\\'. "; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/FileUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/FileUtils.java index e178e2f6047..be99503a3c9 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/FileUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/FileUtils.java @@ -568,6 +568,9 @@ public class FileUtils { } public static String getIllegalError4Directory(final String path) { + if (path == null || path.isEmpty()) { + return UtilMessages.ILLEGAL_EMPTY_PATH; + } if (path.equals(".") || path.equals("..") || path.contains("/") || path.contains("\\")) { return UtilMessages.ILLEGAL_PATH_DOTS_OR_SEPARATORS; } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/WindowsOSUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/WindowsOSUtils.java index 3e8155ed759..d9b2aa2dfa9 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/WindowsOSUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/WindowsOSUtils.java @@ -23,15 +23,27 @@ import org.apache.tsfile.external.commons.lang3.SystemUtils; import java.util.Arrays; import java.util.HashSet; +import java.util.Locale; import java.util.Set; public class WindowsOSUtils { private static final String ILLEGAL_WINDOWS_CHARS = "\\/:*?\"<>|"; private static final Set<String> ILLEGAL_WINDOWS_NAMES = - new HashSet<>(Arrays.asList("CON", "PRN", "AUX", "NUL", "COM1-COM9, LPT1-LPT9")); + new HashSet<>( + Arrays.asList( + "CON", + "PRN", + "AUX", + "NUL", + "COM\u00B9", + "COM\u00B2", + "COM\u00B3", + "LPT\u00B9", + "LPT\u00B2", + "LPT\u00B3")); static { - for (int i = 0; i < 10; ++i) { + for (int i = 1; i < 10; ++i) { ILLEGAL_WINDOWS_NAMES.add("COM" + i); ILLEGAL_WINDOWS_NAMES.add("LPT" + i); } @@ -39,26 +51,39 @@ public class WindowsOSUtils { public static final String OS_SEGMENT_ERROR = String.format( - "In Windows System, the path shall not contains %s, equals one of %s, or ends with '.' or ' '.", + "In Windows System, the path shall not contain %s or ASCII control characters, equals one of %s with or without an extension, or ends with '.' or ' '.", ILLEGAL_WINDOWS_CHARS, ILLEGAL_WINDOWS_NAMES); public static boolean isLegalPathSegment4Windows(final String pathSegment) { if (!SystemUtils.IS_OS_WINDOWS) { return true; } - for (final char illegalChar : ILLEGAL_WINDOWS_CHARS.toCharArray()) { - if (pathSegment.indexOf(illegalChar) != -1) { - return false; - } + if (containsIllegalWindowsChar(pathSegment)) { + return false; } if (pathSegment.endsWith(".") || pathSegment.endsWith(" ")) { return false; } - for (final String illegalName : ILLEGAL_WINDOWS_NAMES) { - if (pathSegment.equalsIgnoreCase(illegalName)) { - return false; - } + if (isIllegalWindowsName(pathSegment)) { + return false; } return true; } + + private static boolean containsIllegalWindowsChar(final String pathSegment) { + for (int i = 0; i < pathSegment.length(); ++i) { + final char ch = pathSegment.charAt(i); + if (ch < ' ' || ILLEGAL_WINDOWS_CHARS.indexOf(ch) != -1) { + return true; + } + } + return false; + } + + private static boolean isIllegalWindowsName(final String pathSegment) { + final int extensionStartIndex = pathSegment.indexOf('.'); + final String nameWithoutExtension = + extensionStartIndex < 0 ? pathSegment : pathSegment.substring(0, extensionStartIndex); + return ILLEGAL_WINDOWS_NAMES.contains(nameWithoutExtension.toUpperCase(Locale.ENGLISH)); + } } diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/FileUtilsTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/FileUtilsTest.java index e25755f4c17..cc1586f5f74 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/FileUtilsTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/FileUtilsTest.java @@ -25,6 +25,7 @@ import org.apache.tsfile.write.TsFileWriter; import org.apache.tsfile.write.record.Tablet; import org.apache.tsfile.write.schema.MeasurementSchema; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -60,6 +61,13 @@ public class FileUtilsTest { FileUtils.moveFileWithMD5Check(tstFile, targetDir); } + @Test + public void testGetIllegalError4DirectoryRejectsEmptyPath() { + Assert.assertNotNull(FileUtils.getIllegalError4Directory(null)); + Assert.assertNotNull(FileUtils.getIllegalError4Directory("")); + Assert.assertNull(FileUtils.getIllegalError4Directory("valid_dir")); + } + private void generateFile(File tsfile) throws WriteProcessException, IOException { try (TsFileWriter writer = new TsFileWriter(tsfile)) { writer.registerAlignedTimeseries( diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/WindowsOSUtilsTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/WindowsOSUtilsTest.java index bbe847b6f8e..270c229e237 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/WindowsOSUtilsTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/WindowsOSUtilsTest.java @@ -37,5 +37,17 @@ public class WindowsOSUtilsTest { Assert.assertFalse(isLegalPathSegment4Windows("C.")); Assert.assertFalse(isLegalPathSegment4Windows("a:b<|")); Assert.assertFalse(isLegalPathSegment4Windows("COM1")); + Assert.assertFalse(isLegalPathSegment4Windows("com1")); + Assert.assertFalse(isLegalPathSegment4Windows("COM1.txt")); + Assert.assertFalse(isLegalPathSegment4Windows("NUL.log")); + Assert.assertFalse(isLegalPathSegment4Windows("LPT9.tmp")); + Assert.assertFalse(isLegalPathSegment4Windows("COM\u00B9")); + Assert.assertFalse(isLegalPathSegment4Windows("LPT\u00B2.log")); + Assert.assertFalse(isLegalPathSegment4Windows("name\tpart")); + Assert.assertFalse(isLegalPathSegment4Windows("name" + Character.toString((char) 0) + "part")); + + Assert.assertTrue(isLegalPathSegment4Windows("COM0")); + Assert.assertTrue(isLegalPathSegment4Windows("LPT0")); + Assert.assertTrue(isLegalPathSegment4Windows("COM\u00B4")); } }
