diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadTsFileIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadTsFileIT.java index 8a0800771cf9f..fa5e0ecc31341 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadTsFileIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadTsFileIT.java @@ -890,6 +890,28 @@ public void testLoadWithRelativePathName() throws Exception { } } + @Test + public void testLoadDataNodeInternalDataDirectoryIsRejectedWithoutLeakingPath() throws Exception { + final DataNodeWrapper dataNodeWrapper = EnvFactory.getEnv().getDataNodeWrapper(0); + final File dataDir = new File(dataNodeWrapper.getDataPath()); + + try (final Connection connection = + EnvFactory.getEnv().getConnectionWithSpecifiedDataNode(dataNodeWrapper); + final Statement statement = connection.createStatement()) { + try { + statement.execute(String.format("load \"%s\"", dataDir.getAbsolutePath())); + Assert.fail("Expected LOAD from the DataNode internal data directory to be rejected."); + } catch (final SQLException e) { + Assert.assertTrue( + e.getMessage(), + e.getMessage() + .contains( + "Cannot load files because the specified directory contains IoTDB data.")); + Assert.assertFalse(e.getMessage(), e.getMessage().contains(dataDir.getAbsolutePath())); + } + } + } + @Test public void testLoadWithMods() throws Exception { final long writtenPoint1; diff --git a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java index e8e1954751bcb..1c7c83061e3b3 100644 --- a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java +++ b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java @@ -3037,6 +3037,9 @@ private DataNodeQueryMessages() {} "Can not find %s on this machine, notice that load can only handle files on this machine."; public static final String QUERY_EXCEPTION_LOAD_TSFILE_SOURCE_PATH_S_IS_OUTSIDE_ALLOWED_DIRECTORIES_85A6019F = "Load TsFile source path %s is outside allowed directories %s."; + public static final String + QUERY_EXCEPTION_CANNOT_LOAD_FILES_BECAUSE_SPECIFIED_DIRECTORY_CONTAINS_IOTDB_DATA_B0A1B93D = + "Cannot load files because the specified directory contains IoTDB data."; public static final String QUERY_EXCEPTION_FAILED_TO_RESOLVE_CANONICAL_PATH_FOR_LOAD_TSFILE_SOURCE_09CC9AC6 = "Failed to resolve canonical path for Load TsFile source %s: %s"; public static final String QUERY_EXCEPTION_DATA_TYPE_IS_NOT_CONSISTENT_INPUT_S_REGISTERED_S_AE9DBDC0 = diff --git a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java index c06b5332f7b2c..ba502738b83de 100644 --- a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java +++ b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java @@ -3654,6 +3654,9 @@ private DataNodeQueryMessages() {} public static final String QUERY_EXCEPTION_LOAD_TSFILE_SOURCE_PATH_S_IS_OUTSIDE_ALLOWED_DIRECTORIES_85A6019F = "加载 TsFile 的源路径 %s 位于允许目录 %s 之外。"; + public static final String + QUERY_EXCEPTION_CANNOT_LOAD_FILES_BECAUSE_SPECIFIED_DIRECTORY_CONTAINS_IOTDB_DATA_B0A1B93D = + "指定目录包含 IoTDB 数据,无法加载文件。"; public static final String QUERY_EXCEPTION_FAILED_TO_RESOLVE_CANONICAL_PATH_FOR_LOAD_TSFILE_SOURCE_09CC9AC6 = "无法解析 load TsFile source %s 的 canonical path:%s"; public static final String QUERY_EXCEPTION_DATA_TYPE_IS_NOT_CONSISTENT_INPUT_S_REGISTERED_S_AE9DBDC0 = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java index cf901ebd2db1f..19c3cc6217f5b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java @@ -310,6 +310,8 @@ public class IoTDBConfig { private CanonicalPaths loadTsFileAllowedDirCanonicalPaths = canonicalPaths(loadTsFileAllowedDirs); + private volatile CanonicalPaths internalDataDirCanonicalPaths = new CanonicalPaths(new Path[0]); + private boolean loadTsFileSourcePathCheckEnabled = false; /** Strategy of multiple directories. */ @@ -1435,6 +1437,7 @@ private void formulateFolders() { queryDir = addDataHomeDir(queryDir); sortTmpDir = addDataHomeDir(sortTmpDir); formulateDataDirs(tierDataDirs); + formulateInternalDataDirs(tierDataDirs); } private void formulateDataDirs(String[][] tierDataDirs) { @@ -1486,6 +1489,7 @@ void reloadDataDirs(String[][] newTierDataDirs) throws LoadConfigurationExceptio } } this.tierDataDirs = newTierDataDirs; + formulateInternalDataDirs(newTierDataDirs); reloadSystemMetrics(); } @@ -1562,6 +1566,10 @@ public String[] getLocalDataDirs() { .toArray(String[]::new); } + public Path[] getInternalDataDirCanonicalPaths() throws FileNotFoundException { + return internalDataDirCanonicalPaths.getPaths(); + } + public String[][] getTierDataDirs() { return tierDataDirs; } @@ -1570,6 +1578,7 @@ public String[][] getTierDataDirs() { public void setTierDataDirs(String[][] tierDataDirs) { formulateDataDirs(tierDataDirs); this.tierDataDirs = tierDataDirs; + formulateInternalDataDirs(tierDataDirs); // TODO(szywilliam): rewrite the logic here when ratis supports complete snapshot semantic setRatisDataRegionSnapshotDir( tierDataDirs[0][0] + File.separator + IoTDBConstant.SNAPSHOT_FOLDER_NAME); @@ -1669,6 +1678,19 @@ public void formulateLoadTsFileDirs(String[][] tierDataDirs) { this.loadTsFileDirCanonicalPaths = canonicalPaths(newLoadTsFileDirs); } + private void formulateInternalDataDirs(final String[][] tierDataDirs) { + final List internalDataDirs = new ArrayList<>(); + internalDataDirs.add(addDataHomeDir("data")); + for (final String[] tierDataDir : tierDataDirs) { + for (final String dataDir : tierDataDir) { + if (FSUtils.isLocal(dataDir)) { + internalDataDirs.add(dataDir); + } + } + } + internalDataDirCanonicalPaths = canonicalPaths(internalDataDirs.toArray(new String[0])); + } + private static CanonicalPaths canonicalPaths(final String[] dirs) { final Path[] paths = new Path[dirs.length]; for (int i = 0; i < dirs.length; i++) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/loader/TsFileLoader.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/loader/TsFileLoader.java index bf6f717be01b9..b91547137fbdf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/loader/TsFileLoader.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/loader/TsFileLoader.java @@ -53,7 +53,7 @@ public TsFileLoader(File tsFile, String database) { @Override public void load(final SessionInfo sessionInfo) { try { - LoadTsFileStatement statement = LoadTsFileStatement.createUnchecked(tsFile.getAbsolutePath()); + LoadTsFileStatement statement = LoadTsFileStatement.createForPipe(tsFile.getAbsolutePath()); statement.setDeleteAfterLoad(true); statement.setConvertOnTypeMismatch(true); statement.setDatabaseLevel(parseSgLevel()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java index 7424f20bb8abb..8257dadfc9992 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java @@ -714,7 +714,7 @@ static LoadTsFileStatement buildLoadTsFileStatementForSync( final boolean shouldConvertDataTypeOnTypeMismatch, final boolean shouldWaitForSchemaBeforeLoad) throws FileNotFoundException { - final LoadTsFileStatement statement = LoadTsFileStatement.createUnchecked(fileAbsolutePath); + final LoadTsFileStatement statement = LoadTsFileStatement.createForPipe(fileAbsolutePath); statement.setDeleteAfterLoad(true); statement.setConvertOnTypeMismatch(shouldConvertDataTypeOnTypeMismatch); statement.setVerifySchema( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java index 34827de0c63da..68b23ae5d00c8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java @@ -507,15 +507,20 @@ private boolean handleSingleMiniFile(final int i) throws FileNotFoundException { isTableModelTsFile.get(i) ? loadTsFileDataTypeConverter .convertForTableModel( - LoadTsFile.createUnchecked( - null, tsFiles.get(i).getPath(), Collections.emptyMap()) + (isGeneratedByPipe + ? LoadTsFile.createForPipe( + null, tsFiles.get(i).getPath(), Collections.emptyMap()) + : LoadTsFile.createUnchecked( + null, tsFiles.get(i).getPath(), Collections.emptyMap())) .setDatabase(databaseForTableData) .setDeleteAfterLoad(isDeleteAfterLoad) .setConvertOnTypeMismatch(isConvertOnTypeMismatch)) .orElse(null) : loadTsFileDataTypeConverter .convertForTreeModel( - LoadTsFileStatement.createUnchecked(tsFiles.get(i).getPath()) + (isGeneratedByPipe + ? LoadTsFileStatement.createForPipe(tsFiles.get(i).getPath()) + : LoadTsFileStatement.createUnchecked(tsFiles.get(i).getPath())) .setDeleteAfterLoad(isDeleteAfterLoad) .setConvertOnTypeMismatch(isConvertOnTypeMismatch)) .orElse(null); @@ -800,15 +805,20 @@ private void executeTabletConversionOnException( isTableModelTsFile.get(i) ? loadTsFileDataTypeConverter .convertForTableModel( - LoadTsFile.createUnchecked( - null, tsFiles.get(i).getPath(), Collections.emptyMap()) + (isGeneratedByPipe + ? LoadTsFile.createForPipe( + null, tsFiles.get(i).getPath(), Collections.emptyMap()) + : LoadTsFile.createUnchecked( + null, tsFiles.get(i).getPath(), Collections.emptyMap())) .setDatabase(databaseForTableData) .setDeleteAfterLoad(isDeleteAfterLoad) .setConvertOnTypeMismatch(isConvertOnTypeMismatch)) .orElse(null) : loadTsFileDataTypeConverter .convertForTreeModel( - LoadTsFileStatement.createUnchecked(tsFiles.get(i).getPath()) + (isGeneratedByPipe + ? LoadTsFileStatement.createForPipe(tsFiles.get(i).getPath()) + : LoadTsFileStatement.createUnchecked(tsFiles.get(i).getPath())) .setDeleteAfterLoad(isDeleteAfterLoad) .setConvertOnTypeMismatch(isConvertOnTypeMismatch)) .orElse(null); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java index 17f958249291e..8e46f7cd72a12 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java @@ -27,6 +27,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.db.queryengine.plan.statement.crud.LoadTsFileStatement; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; import org.apache.iotdb.db.storageengine.load.config.LoadTsFileConfigurator; @@ -74,19 +75,25 @@ public class LoadTsFile extends Statement { private boolean needDecode4TimeColumn; public LoadTsFile(NodeLocation location, String filePath, Map loadAttributes) { - this(location, filePath, loadAttributes, true); + this(location, filePath, loadAttributes, true, true); } public static LoadTsFile createUnchecked( NodeLocation location, String filePath, Map loadAttributes) { - return new LoadTsFile(location, filePath, loadAttributes, false); + return new LoadTsFile(location, filePath, loadAttributes, false, true); + } + + public static LoadTsFile createForPipe( + NodeLocation location, String filePath, Map loadAttributes) { + return new LoadTsFile(location, filePath, loadAttributes, false, false); } private LoadTsFile( NodeLocation location, String filePath, Map loadAttributes, - boolean validateSourcePath) { + boolean validateSourcePath, + boolean validateInternalDataDir) { super(location); this.filePath = requireNonNull(filePath, DataNodeQueryMessages.EXCEPTION_FILEPATH_IS_NULL_84CE8A66); @@ -105,8 +112,9 @@ private LoadTsFile( try { this.tsFiles = - org.apache.iotdb.db.queryengine.plan.statement.crud.LoadTsFileStatement.processTsFile( - new File(filePath), validateSourcePath); + validateInternalDataDir + ? LoadTsFileStatement.processTsFile(new File(filePath), validateSourcePath) + : LoadTsFileStatement.processTsFileForPipe(new File(filePath)); this.resources = new ArrayList<>(); this.writePointCountList = new ArrayList<>(); this.isTableModel = new ArrayList<>(Collections.nCopies(this.tsFiles.size(), true)); @@ -306,7 +314,9 @@ public List getSubStatements() { final Map properties = this.loadAttributes; final LoadTsFile subStatement = - LoadTsFile.createUnchecked(getLocation().orElse(null), filePath, properties); + isGeneratedByPipe + ? LoadTsFile.createForPipe(getLocation().orElse(null), filePath, properties) + : LoadTsFile.createUnchecked(getLocation().orElse(null), filePath, properties); // Copy all configuration properties subStatement.databaseLevel = this.databaseLevel; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/load/LoadTsFileScheduler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/load/LoadTsFileScheduler.java index ef2e948c0e6c2..da143c6b8cb40 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/load/LoadTsFileScheduler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/load/LoadTsFileScheduler.java @@ -625,7 +625,10 @@ private void convertFailedTsFilesToTabletsAndRetry() { failedNode.isTableModel() ? loadTsFileDataTypeConverter .convertForTableModel( - LoadTsFile.createUnchecked(null, filePath, Collections.emptyMap()) + (isGeneratedByPipe + ? LoadTsFile.createForPipe(null, filePath, Collections.emptyMap()) + : LoadTsFile.createUnchecked( + null, filePath, Collections.emptyMap())) .setDatabase(failedNode.getDatabase()) .setDeleteAfterLoad(failedNode.isDeleteAfterLoad()) .setConvertOnTypeMismatch(true)) @@ -684,7 +687,9 @@ private LoadTsFileStatement buildRetryTreeLoadStatement( final String filePath, final boolean deleteAfterLoad, final String database) throws FileNotFoundException { final LoadTsFileStatement statement = - LoadTsFileStatement.createUnchecked(filePath) + (isGeneratedByPipe + ? LoadTsFileStatement.createForPipe(filePath) + : LoadTsFileStatement.createUnchecked(filePath)) .setDeleteAfterLoad(deleteAfterLoad) .setConvertOnTypeMismatch(true); if (database != null) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/LoadTsFileStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/LoadTsFileStatement.java index ca823fcb5579e..0b0dea7631121 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/LoadTsFileStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/LoadTsFileStatement.java @@ -80,14 +80,19 @@ public class LoadTsFileStatement extends Statement { private boolean needDecode4TimeColumn; public LoadTsFileStatement(String filePath) throws FileNotFoundException { - this(filePath, true); + this(filePath, true, true); } public static LoadTsFileStatement createUnchecked(String filePath) throws FileNotFoundException { - return new LoadTsFileStatement(filePath, false); + return new LoadTsFileStatement(filePath, false, true); } - private LoadTsFileStatement(String filePath, boolean validateSourcePath) + public static LoadTsFileStatement createForPipe(String filePath) throws FileNotFoundException { + return new LoadTsFileStatement(filePath, false, false); + } + + private LoadTsFileStatement( + String filePath, boolean validateSourcePath, boolean validateInternalDataDir) throws FileNotFoundException { this.file = new File(filePath).getAbsoluteFile(); this.databaseLevel = IoTDBDescriptor.getInstance().getConfig().getDefaultDatabaseLevel(); @@ -99,7 +104,7 @@ private LoadTsFileStatement(String filePath, boolean validateSourcePath) IoTDBDescriptor.getInstance().getConfig().getLoadTabletConversionThresholdBytes(); this.autoCreateDatabase = IoTDBDescriptor.getInstance().getConfig().isAutoCreateSchemaEnabled(); - this.tsFiles = processTsFile(file, validateSourcePath); + this.tsFiles = processTsFile(file, validateSourcePath, validateInternalDataDir); this.resources = new ArrayList<>(); this.writePointCountList = new ArrayList<>(); this.isTableModel = new ArrayList<>(Collections.nCopies(this.tsFiles.size(), false)); @@ -107,11 +112,26 @@ private LoadTsFileStatement(String filePath, boolean validateSourcePath) } public static List processTsFile(final File file) throws FileNotFoundException { - return processTsFile(file, true); + return processTsFile(file, true, true); } public static List processTsFile(final File file, final boolean validateSourcePath) throws FileNotFoundException { + return processTsFile(file, validateSourcePath, true); + } + + public static List processTsFileForPipe(final File file) throws FileNotFoundException { + return processTsFile(file, false, false); + } + + private static List processTsFile( + final File file, final boolean validateSourcePath, final boolean validateInternalDataDir) + throws FileNotFoundException { + final Path[] internalDataDirCanonicalPaths = + IoTDBDescriptor.getInstance().getConfig().getInternalDataDirCanonicalPaths(); + if (validateInternalDataDir) { + validateNotLoadingInternalTsFile(file, internalDataDirCanonicalPaths); + } if (validateSourcePath) { validateLoadSourcePath(file); } @@ -127,7 +147,9 @@ public static List processTsFile(final File file, final boolean validateSo .QUERY_EXCEPTION_CAN_NOT_FIND_S_ON_THIS_MACHINE_NOTICE_THAT_LOAD_CAN_ONLY_B7886C0E, file.getPath())); } - tsFiles.addAll(findAllTsFile(file, validateSourcePath)); + tsFiles.addAll( + findAllTsFile( + file, validateSourcePath, validateInternalDataDir, internalDataDirCanonicalPaths)); } sortTsFiles(tsFiles); return tsFiles; @@ -150,7 +172,11 @@ protected LoadTsFileStatement() { this.statementType = StatementType.MULTI_BATCH_INSERT; } - private static List findAllTsFile(File file, boolean validateSourcePath) + private static List findAllTsFile( + File file, + boolean validateSourcePath, + boolean validateInternalDataDir, + Path[] internalDataDirCanonicalPaths) throws FileNotFoundException { final File[] files = file.listFiles(); if (files == null) { @@ -159,13 +185,21 @@ private static List findAllTsFile(File file, boolean validateSourcePath) final List tsFiles = new ArrayList<>(); for (File nowFile : files) { + if (validateInternalDataDir) { + validateNotLoadingInternalTsFile(nowFile, internalDataDirCanonicalPaths); + } if (validateSourcePath) { validateLoadSourcePath(nowFile); } if (nowFile.getName().endsWith(TsFileConstant.TSFILE_SUFFIX)) { tsFiles.add(nowFile); } else if (nowFile.isDirectory()) { - tsFiles.addAll(findAllTsFile(nowFile, validateSourcePath)); + tsFiles.addAll( + findAllTsFile( + nowFile, + validateSourcePath, + validateInternalDataDir, + internalDataDirCanonicalPaths)); } } return tsFiles; @@ -199,6 +233,19 @@ private static void validateLoadSourcePath(final File file) throws FileNotFoundE Arrays.toString(allowedDirs))); } + private static void validateNotLoadingInternalTsFile( + final File file, final Path[] internalDataDirCanonicalPaths) throws FileNotFoundException { + final Path sourcePath = canonicalPath(file); + for (final Path internalDataDirCanonicalPath : internalDataDirCanonicalPaths) { + if (sourcePath.startsWith(internalDataDirCanonicalPath) + || internalDataDirCanonicalPath.startsWith(sourcePath)) { + throw new FileNotFoundException( + DataNodeQueryMessages + .QUERY_EXCEPTION_CANNOT_LOAD_FILES_BECAUSE_SPECIFIED_DIRECTORY_CONTAINS_IOTDB_DATA_B0A1B93D); + } + } + } + private static Path canonicalPath(final File file) throws FileNotFoundException { try { return file.getCanonicalFile().toPath(); @@ -498,7 +545,9 @@ public List getPaths() { loadAttributes.put(PIPE_GENERATED_KEY, String.valueOf(true)); } - return LoadTsFile.createUnchecked(null, file.getAbsolutePath(), loadAttributes); + return isGeneratedByPipe + ? LoadTsFile.createForPipe(null, file.getAbsolutePath(), loadAttributes) + : LoadTsFile.createUnchecked(null, file.getAbsolutePath(), loadAttributes); } @Override diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/crud/LoadTsFileStatementTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/crud/LoadTsFileStatementTest.java index bfebf51d28191..fac3227f4b5e3 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/crud/LoadTsFileStatementTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/crud/LoadTsFileStatementTest.java @@ -109,6 +109,66 @@ public void testLoadSourcePathCheckCanBeDisabled() throws Exception { } } + @Test + public void testLoadInternalTsFileIsRejectedWithoutLeakingPath() throws Exception { + final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); + final String[][] originalTierDataDirs = config.getTierDataDirs(); + final boolean originalCheckEnabled = config.isLoadTsFileSourcePathCheckEnabled(); + final Path dataNodeDir = Files.createTempDirectory("load-tsfile-datanode"); + final Path dataDir = dataNodeDir.resolve("data"); + final Path internalTsFile = + Files.createDirectories(dataDir.resolve("pipe-hardlink")).resolve("a.tsfile"); + Files.createFile(internalTsFile); + + try { + config.setTierDataDirs(new String[][] {{dataDir.toString()}}); + config.setLoadTsFileSourcePathCheckEnabled(false); + + try { + new LoadTsFileStatement(internalTsFile.toString()); + Assert.fail("Expected internal IoTDB data directory to be rejected."); + } catch (final FileNotFoundException e) { + Assert.assertEquals( + "Cannot load files because the specified directory contains IoTDB data.", + e.getMessage()); + Assert.assertFalse(e.getMessage().contains(dataDir.toString())); + Assert.assertFalse(e.getMessage().contains(internalTsFile.toString())); + } + + Assert.assertEquals( + 1, LoadTsFileStatement.createForPipe(internalTsFile.toString()).getTsFiles().size()); + } finally { + config.setTierDataDirs(originalTierDataDirs); + config.setLoadTsFileSourcePathCheckEnabled(originalCheckEnabled); + deleteRecursively(dataNodeDir); + } + } + + @Test + public void testLoadPipeReceiverTsFileOutsideDataDirIsAllowed() throws Exception { + final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); + final String[][] originalTierDataDirs = config.getTierDataDirs(); + final boolean originalCheckEnabled = config.isLoadTsFileSourcePathCheckEnabled(); + final Path dataNodeDir = Files.createTempDirectory("load-tsfile-datanode"); + final Path dataDir = dataNodeDir.resolve("data"); + final Path pipeReceiverDir = + Files.createDirectories(dataNodeDir.resolve("system").resolve("pipe").resolve("receiver")); + final Path pipeReceiverTsFile = Files.createFile(pipeReceiverDir.resolve("a.tsfile")); + + try { + config.setTierDataDirs(new String[][] {{dataDir.toString()}}); + config.setLoadTsFileSourcePathCheckEnabled(false); + + final LoadTsFileStatement statement = new LoadTsFileStatement(pipeReceiverTsFile.toString()); + Assert.assertEquals(1, statement.getTsFiles().size()); + Assert.assertEquals(pipeReceiverTsFile.toFile(), statement.getTsFiles().get(0)); + } finally { + config.setTierDataDirs(originalTierDataDirs); + config.setLoadTsFileSourcePathCheckEnabled(originalCheckEnabled); + deleteRecursively(dataNodeDir); + } + } + private static void assertLoadSourcePathRejected(final Path sourcePath) { try { new LoadTsFileStatement(sourcePath.toString());