You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by lz...@apache.org on 2022/11/23 02:42:48 UTC

[flink-table-store] branch master updated: [FLINK-30139] CodeGenLoader fails when temporary directory is a symlink

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

lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-table-store.git


The following commit(s) were added to refs/heads/master by this push:
     new ae96b335 [FLINK-30139] CodeGenLoader fails when temporary directory is a symlink
ae96b335 is described below

commit ae96b335e470a068a7e87f3d1a22c58bf2d82256
Author: Jingsong Lee <ji...@gmail.com>
AuthorDate: Wed Nov 23 10:42:42 2022 +0800

    [FLINK-30139] CodeGenLoader fails when temporary directory is a symlink
    
    This closes #397
---
 flink-table-store-codegen-loader/pom.xml           |   7 ++
 .../flink/table/store/codegen/CodeGenLoader.java   |  16 ++--
 .../flink/table/store/utils/LocalFileUtils.java    |  50 +++++++++++
 .../table/store/utils/LocalFileUtilsTest.java      |  80 +++++++++++++++++
 .../flink/table/store/utils/TempDirUtils.java      | 100 +++++++++++++++++++++
 5 files changed, 247 insertions(+), 6 deletions(-)

diff --git a/flink-table-store-codegen-loader/pom.xml b/flink-table-store-codegen-loader/pom.xml
index b968b507..1cbe9bd7 100644
--- a/flink-table-store-codegen-loader/pom.xml
+++ b/flink-table-store-codegen-loader/pom.xml
@@ -39,6 +39,13 @@ under the License.
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-table-store-common</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
         <dependency>
             <!-- Ensures that flink-table-store-codegen is built beforehand, in order to bundle the jar -->
             <groupId>org.apache.flink</groupId>
diff --git a/flink-table-store-codegen-loader/src/main/java/org/apache/flink/table/store/codegen/CodeGenLoader.java b/flink-table-store-codegen-loader/src/main/java/org/apache/flink/table/store/codegen/CodeGenLoader.java
index 938e4cec..6bba6a4c 100644
--- a/flink-table-store-codegen-loader/src/main/java/org/apache/flink/table/store/codegen/CodeGenLoader.java
+++ b/flink-table-store-codegen-loader/src/main/java/org/apache/flink/table/store/codegen/CodeGenLoader.java
@@ -20,6 +20,7 @@ package org.apache.flink.table.store.codegen;
 
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.configuration.ConfigurationUtils;
+import org.apache.flink.table.store.utils.LocalFileUtils;
 import org.apache.flink.util.IOUtils;
 
 import java.io.IOException;
@@ -70,7 +71,8 @@ public class CodeGenLoader {
             final ClassLoader flinkClassLoader = CodeGenLoader.class.getClassLoader();
             final Path tmpDirectory =
                     Paths.get(ConfigurationUtils.parseTempDirectories(new Configuration())[0]);
-            Files.createDirectories(tmpDirectory);
+            Files.createDirectories(
+                    LocalFileUtils.getTargetPathIfContainsSymbolicPath(tmpDirectory));
             Path delegateJar =
                     extractResource(
                             FLINK_TABLE_STORE_CODEGEN_FAT_JAR,
@@ -110,12 +112,14 @@ public class CodeGenLoader {
 
     // Singleton lazy initialization
 
-    private static class CodegenLoaderHolder {
-        private static final CodeGenLoader INSTANCE = new CodeGenLoader();
-    }
+    private static CodeGenLoader instance;
 
-    public static CodeGenLoader getInstance() {
-        return CodeGenLoader.CodegenLoaderHolder.INSTANCE;
+    public static synchronized CodeGenLoader getInstance() {
+        if (instance == null) {
+            // Avoid NoClassDefFoundError without cause by exception
+            instance = new CodeGenLoader();
+        }
+        return instance;
     }
 
     public <T> T discover(Class<T> clazz) {
diff --git a/flink-table-store-common/src/main/java/org/apache/flink/table/store/utils/LocalFileUtils.java b/flink-table-store-common/src/main/java/org/apache/flink/table/store/utils/LocalFileUtils.java
new file mode 100644
index 00000000..a3930ea7
--- /dev/null
+++ b/flink-table-store-common/src/main/java/org/apache/flink/table/store/utils/LocalFileUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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.flink.table.store.utils;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/** Utils for local file. */
+public class LocalFileUtils {
+
+    /**
+     * Get a target path(the path that replaced symbolic links with linked path) if the original
+     * path contains symbolic path, return the original path otherwise.
+     *
+     * @param path the original path.
+     * @return the path that replaced symbolic links with real path.
+     */
+    public static Path getTargetPathIfContainsSymbolicPath(Path path) throws IOException {
+        Path targetPath = path;
+        Path suffixPath = Paths.get("");
+        while (path != null && path.getFileName() != null) {
+            if (Files.isSymbolicLink(path)) {
+                Path linkedPath = path.toRealPath();
+                targetPath = Paths.get(linkedPath.toString(), suffixPath.toString());
+                break;
+            }
+            suffixPath = Paths.get(path.getFileName().toString(), suffixPath.toString());
+            path = path.getParent();
+        }
+        return targetPath;
+    }
+}
diff --git a/flink-table-store-common/src/test/java/org/apache/flink/table/store/utils/LocalFileUtilsTest.java b/flink-table-store-common/src/test/java/org/apache/flink/table/store/utils/LocalFileUtilsTest.java
new file mode 100644
index 00000000..b4bbed32
--- /dev/null
+++ b/flink-table-store-common/src/test/java/org/apache/flink/table/store/utils/LocalFileUtilsTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.flink.table.store.utils;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Test for {@link LocalFileUtils}. */
+public class LocalFileUtilsTest {
+
+    @TempDir private java.nio.file.Path temporaryFolder;
+
+    @Test
+    void testGetTargetPathNotContainsSymbolicPath() throws IOException {
+        java.nio.file.Path testPath = Paths.get("parent", "child");
+        java.nio.file.Path targetPath =
+                LocalFileUtils.getTargetPathIfContainsSymbolicPath(testPath);
+        assertThat(targetPath).isEqualTo(testPath);
+    }
+
+    @Test
+    void testGetTargetPathContainsSymbolicPath() throws IOException {
+        File linkedDir = TempDirUtils.newFolder(temporaryFolder, "linked");
+        java.nio.file.Path symlink = Paths.get(temporaryFolder.toString(), "symlink");
+        java.nio.file.Path dirInLinked =
+                TempDirUtils.newFolder(linkedDir.toPath(), "one", "two").toPath().toRealPath();
+        Files.createSymbolicLink(symlink, linkedDir.toPath());
+
+        java.nio.file.Path targetPath =
+                LocalFileUtils.getTargetPathIfContainsSymbolicPath(
+                        symlink.resolve("one").resolve("two"));
+        assertThat(targetPath).isEqualTo(dirInLinked);
+    }
+
+    @Test
+    void testGetTargetPathContainsMultipleSymbolicPath() throws IOException {
+        File linked1Dir = TempDirUtils.newFolder(temporaryFolder, "linked1");
+        java.nio.file.Path symlink1 = Paths.get(temporaryFolder.toString(), "symlink1");
+        Files.createSymbolicLink(symlink1, linked1Dir.toPath());
+
+        java.nio.file.Path symlink2 = Paths.get(symlink1.toString(), "symlink2");
+        File linked2Dir = TempDirUtils.newFolder(temporaryFolder, "linked2");
+        Files.createSymbolicLink(symlink2, linked2Dir.toPath());
+        java.nio.file.Path dirInLinked2 =
+                TempDirUtils.newFolder(linked2Dir.toPath(), "one").toPath().toRealPath();
+
+        // symlink3 point to another symbolic link: symlink2
+        java.nio.file.Path symlink3 = Paths.get(symlink1.toString(), "symlink3");
+        Files.createSymbolicLink(symlink3, symlink2);
+
+        java.nio.file.Path targetPath =
+                LocalFileUtils.getTargetPathIfContainsSymbolicPath(
+                        // path contains multiple symlink : xxx/symlink1/symlink3/one
+                        symlink3.resolve("one"));
+        assertThat(targetPath).isEqualTo(dirInLinked2);
+    }
+}
diff --git a/flink-table-store-common/src/test/java/org/apache/flink/table/store/utils/TempDirUtils.java b/flink-table-store-common/src/test/java/org/apache/flink/table/store/utils/TempDirUtils.java
new file mode 100644
index 00000000..fecb457b
--- /dev/null
+++ b/flink-table-store-common/src/test/java/org/apache/flink/table/store/utils/TempDirUtils.java
@@ -0,0 +1,100 @@
+/*
+ * 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.flink.table.store.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/** The utils contains some methods same as org.junit.rules.TemporaryFolder in Junit4. */
+public class TempDirUtils {
+    private static final String TMP_PREFIX = "junit";
+
+    public static File newFolder(Path path) throws IOException {
+        Path tempPath;
+        if (path != null) {
+            tempPath = Files.createTempDirectory(path, TMP_PREFIX);
+        } else {
+            tempPath = Files.createTempDirectory(TMP_PREFIX);
+        }
+        return tempPath.toFile();
+    }
+
+    public static File newFile(Path path) throws IOException {
+        return File.createTempFile(TMP_PREFIX, null, path.toFile());
+    }
+
+    public static File newFolder(Path base, String... paths) throws IOException {
+        if (paths.length == 0) {
+            throw new IllegalArgumentException("must pass at least one path");
+        }
+
+        /*
+         * Before checking if the paths are absolute paths, check if create() was ever called,
+         * and if it wasn't, throw IllegalStateException.
+         */
+        File root = base.toFile();
+        for (String path : paths) {
+            if (new File(path).isAbsolute()) {
+                throw new IOException(
+                        String.format("folder path '%s' is not a relative path", path));
+            }
+        }
+
+        File relativePath = null;
+        File file = root;
+        boolean lastMkdirsCallSuccessful = true;
+        for (String path : paths) {
+            relativePath = new File(relativePath, path);
+            file = new File(root, relativePath.getPath());
+
+            lastMkdirsCallSuccessful = file.mkdirs();
+            if (!lastMkdirsCallSuccessful && !file.isDirectory()) {
+                if (file.exists()) {
+                    throw new IOException(
+                            String.format(
+                                    "a file with the path '%s' exists", relativePath.getPath()));
+                } else {
+                    throw new IOException(
+                            String.format(
+                                    "could not create a folder with the path: '%s'",
+                                    relativePath.getPath()));
+                }
+            }
+        }
+        if (!lastMkdirsCallSuccessful) {
+            throw new IOException(
+                    String.format(
+                            "a folder with the path '%s' already exists", relativePath.getPath()));
+        }
+        return file;
+    }
+
+    public static File newFile(Path folder, String fileName) throws IOException {
+        File file = new File(folder.toFile(), fileName);
+        if (!file.createNewFile()) {
+            throw new IOException(
+                    String.format(
+                            "a file with the name '%s' already exists in the test folder",
+                            fileName));
+        }
+        return file;
+    }
+}