Skip to content

Commit d906c6a

Browse files
authored
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 3d98ea5)
1 parent de0a48f commit d906c6a

4 files changed

Lines changed: 101 additions & 11 deletions

File tree

iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/FileUtils.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public class FileUtils {
5454
"Renamed file {} to {} because it already exists in the target directory: {}";
5555
private static final String COPY_FILE_MESSAGE =
5656
"Copy file {} to {} because it already exists in the target directory: {}";
57+
private static final String ILLEGAL_EMPTY_PATH_MESSAGE = "The path cannot be empty. ";
5758
private static final String ILLEGAL_PATH_MESSAGE =
5859
"The path cannot be '.', '..', './' or '.\\'. ";
5960

@@ -556,6 +557,9 @@ private static void copyFileRename(final File sourceFile, final File targetFile)
556557
}
557558

558559
public static String getIllegalError4Directory(final String path) {
560+
if (path == null || path.isEmpty()) {
561+
return ILLEGAL_EMPTY_PATH_MESSAGE;
562+
}
559563
if (path.equals(".") || path.equals("..") || path.contains("/") || path.contains("\\")) {
560564
return ILLEGAL_PATH_MESSAGE;
561565
}

iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/WindowsOSUtils.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,42 +23,67 @@
2323

2424
import java.util.Arrays;
2525
import java.util.HashSet;
26+
import java.util.Locale;
2627
import java.util.Set;
2728

2829
public class WindowsOSUtils {
2930
private static final String ILLEGAL_WINDOWS_CHARS = "\\/:*?\"<>|";
3031
private static final Set<String> ILLEGAL_WINDOWS_NAMES =
31-
new HashSet<>(Arrays.asList("CON", "PRN", "AUX", "NUL", "COM1-COM9, LPT1-LPT9"));
32+
new HashSet<>(
33+
Arrays.asList(
34+
"CON",
35+
"PRN",
36+
"AUX",
37+
"NUL",
38+
"COM\u00B9",
39+
"COM\u00B2",
40+
"COM\u00B3",
41+
"LPT\u00B9",
42+
"LPT\u00B2",
43+
"LPT\u00B3"));
3244

3345
static {
34-
for (int i = 0; i < 10; ++i) {
46+
for (int i = 1; i < 10; ++i) {
3547
ILLEGAL_WINDOWS_NAMES.add("COM" + i);
3648
ILLEGAL_WINDOWS_NAMES.add("LPT" + i);
3749
}
3850
}
3951

4052
public static final String OS_SEGMENT_ERROR =
4153
String.format(
42-
"In Windows System, the path shall not contains %s, equals one of %s, or ends with '.' or ' '.",
54+
"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 ' '.",
4355
ILLEGAL_WINDOWS_CHARS, ILLEGAL_WINDOWS_NAMES);
4456

4557
public static boolean isLegalPathSegment4Windows(final String pathSegment) {
4658
if (!SystemUtils.IS_OS_WINDOWS) {
4759
return true;
4860
}
49-
for (final char illegalChar : ILLEGAL_WINDOWS_CHARS.toCharArray()) {
50-
if (pathSegment.indexOf(illegalChar) != -1) {
51-
return false;
52-
}
61+
if (containsIllegalWindowsChar(pathSegment)) {
62+
return false;
5363
}
5464
if (pathSegment.endsWith(".") || pathSegment.endsWith(" ")) {
5565
return false;
5666
}
57-
for (final String illegalName : ILLEGAL_WINDOWS_NAMES) {
58-
if (pathSegment.equalsIgnoreCase(illegalName)) {
59-
return false;
60-
}
67+
if (isIllegalWindowsName(pathSegment)) {
68+
return false;
6169
}
6270
return true;
6371
}
72+
73+
private static boolean containsIllegalWindowsChar(final String pathSegment) {
74+
for (int i = 0; i < pathSegment.length(); ++i) {
75+
final char ch = pathSegment.charAt(i);
76+
if (ch < ' ' || ILLEGAL_WINDOWS_CHARS.indexOf(ch) != -1) {
77+
return true;
78+
}
79+
}
80+
return false;
81+
}
82+
83+
private static boolean isIllegalWindowsName(final String pathSegment) {
84+
final int extensionStartIndex = pathSegment.indexOf('.');
85+
final String nameWithoutExtension =
86+
extensionStartIndex < 0 ? pathSegment : pathSegment.substring(0, extensionStartIndex);
87+
return ILLEGAL_WINDOWS_NAMES.contains(nameWithoutExtension.toUpperCase(Locale.ENGLISH));
88+
}
6489
}

iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/FileUtilsTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.tsfile.write.record.Tablet;
2727
import org.apache.tsfile.write.schema.MeasurementSchema;
2828
import org.junit.After;
29+
import org.junit.Assert;
2930
import org.junit.Before;
3031
import org.junit.Test;
3132

@@ -61,6 +62,13 @@ public void testFileUtils() throws WriteProcessException, IOException {
6162
FileUtils.moveFileWithMD5Check(tstFile, targetDir);
6263
}
6364

65+
@Test
66+
public void testGetIllegalError4DirectoryRejectsEmptyPath() {
67+
Assert.assertNotNull(FileUtils.getIllegalError4Directory(null));
68+
Assert.assertNotNull(FileUtils.getIllegalError4Directory(""));
69+
Assert.assertNull(FileUtils.getIllegalError4Directory("valid_dir"));
70+
}
71+
6472
private void generateFile(File tsfile) throws WriteProcessException, IOException {
6573
try (TsFileWriter writer = new TsFileWriter(tsfile)) {
6674
writer.registerAlignedTimeseries(
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.commons.utils;
21+
22+
import org.apache.commons.lang3.SystemUtils;
23+
import org.junit.Assert;
24+
import org.junit.Test;
25+
26+
import static org.apache.iotdb.commons.utils.WindowsOSUtils.isLegalPathSegment4Windows;
27+
28+
public class WindowsOSUtilsTest {
29+
@Test
30+
public void testIllegalDetection() {
31+
if (!SystemUtils.IS_OS_WINDOWS) {
32+
return;
33+
}
34+
Assert.assertTrue(isLegalPathSegment4Windows("abc"));
35+
Assert.assertTrue(isLegalPathSegment4Windows(".A!"));
36+
37+
Assert.assertFalse(isLegalPathSegment4Windows("C."));
38+
Assert.assertFalse(isLegalPathSegment4Windows("a:b<|"));
39+
Assert.assertFalse(isLegalPathSegment4Windows("COM1"));
40+
Assert.assertFalse(isLegalPathSegment4Windows("com1"));
41+
Assert.assertFalse(isLegalPathSegment4Windows("COM1.txt"));
42+
Assert.assertFalse(isLegalPathSegment4Windows("NUL.log"));
43+
Assert.assertFalse(isLegalPathSegment4Windows("LPT9.tmp"));
44+
Assert.assertFalse(isLegalPathSegment4Windows("COM\u00B9"));
45+
Assert.assertFalse(isLegalPathSegment4Windows("LPT\u00B2.log"));
46+
Assert.assertFalse(isLegalPathSegment4Windows("name\tpart"));
47+
Assert.assertFalse(isLegalPathSegment4Windows("name" + Character.toString((char) 0) + "part"));
48+
49+
Assert.assertTrue(isLegalPathSegment4Windows("COM0"));
50+
Assert.assertTrue(isLegalPathSegment4Windows("LPT0"));
51+
Assert.assertTrue(isLegalPathSegment4Windows("COM\u00B4"));
52+
}
53+
}

0 commit comments

Comments
 (0)