This is an automated email from the ASF dual-hosted git repository.

jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/dev/1.3 by this push:
     new d906c6a496b Fix Windows path segment validation (#17868) (#17888)
d906c6a496b is described below

commit d906c6a496bdeb5bc45b178226126f27d0940d68
Author: Caideyipi <[email protected]>
AuthorDate: Wed Jun 10 12:28:18 2026 +0800

    Fix Windows path segment validation (#17868) (#17888)
    
    * Fix Windows reserved device name validation
    
    * Reject additional illegal Windows path segments
    
    * Reject empty directory path segments
    
    (cherry picked from commit 3d98ea5fd7876c4f8f96033457824f10842100c8)
---
 .../org/apache/iotdb/commons/utils/FileUtils.java  |  4 ++
 .../apache/iotdb/commons/utils/WindowsOSUtils.java | 47 ++++++++++++++-----
 .../apache/iotdb/commons/utils/FileUtilsTest.java  |  8 ++++
 .../iotdb/commons/utils/WindowsOSUtilsTest.java    | 53 ++++++++++++++++++++++
 4 files changed, 101 insertions(+), 11 deletions(-)

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 fe413bf85ef..557adf43f62 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
@@ -54,6 +54,7 @@ public class FileUtils {
       "Renamed file {} to {} because it already exists in the target 
directory: {}";
   private static final String COPY_FILE_MESSAGE =
       "Copy file {} to {} because it already exists in the target directory: 
{}";
+  private static final String ILLEGAL_EMPTY_PATH_MESSAGE = "The path cannot be 
empty. ";
   private static final String ILLEGAL_PATH_MESSAGE =
       "The path cannot be '.', '..', './' or '.\\'. ";
 
@@ -556,6 +557,9 @@ public class FileUtils {
   }
 
   public static String getIllegalError4Directory(final String path) {
+    if (path == null || path.isEmpty()) {
+      return ILLEGAL_EMPTY_PATH_MESSAGE;
+    }
     if (path.equals(".") || path.equals("..") || path.contains("/") || 
path.contains("\\")) {
       return ILLEGAL_PATH_MESSAGE;
     }
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 3019c47cf0e..0be89f92747 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.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 c5384e07fad..f4b1ed962bc 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
@@ -26,6 +26,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;
 
@@ -61,6 +62,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
new file mode 100644
index 00000000000..f088ba80730
--- /dev/null
+++ 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/WindowsOSUtilsTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.commons.utils;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static 
org.apache.iotdb.commons.utils.WindowsOSUtils.isLegalPathSegment4Windows;
+
+public class WindowsOSUtilsTest {
+  @Test
+  public void testIllegalDetection() {
+    if (!SystemUtils.IS_OS_WINDOWS) {
+      return;
+    }
+    Assert.assertTrue(isLegalPathSegment4Windows("abc"));
+    Assert.assertTrue(isLegalPathSegment4Windows(".A!"));
+
+    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"));
+  }
+}

Reply via email to