From 5799f8b3d0a981381158f500d4b3af58d66351b4 Mon Sep 17 00:00:00 2001 From: Ralf Schmelter Date: Tue, 14 Apr 2026 14:24:50 +0200 Subject: [PATCH 1/2] Initial implementation. --- .../internal/access/JdkNioZipfsAccess.java | 36 ++++++++++++ .../jdk/internal/access/SharedSecrets.java | 12 ++++ src/java.base/share/classes/module-info.java | 2 + .../com/sap/jdk/ext/util/ZipfsUtils.java | 52 +++++++++++++++++ .../classes/jdk/nio/zipfs/ZipFileSystem.java | 13 +++++ .../share/classes/jdk/nio/zipfs/ZipPath.java | 19 ++++++ test/jdk/sap/ZipfsUtilsTest.java | 55 ++++++++++++++++++ test/jdk/sap/zip-with-symlink.zip | Bin 0 -> 306 bytes 8 files changed, 189 insertions(+) create mode 100644 src/java.base/share/classes/jdk/internal/access/JdkNioZipfsAccess.java create mode 100644 src/jdk.sapext/share/classes/com/sap/jdk/ext/util/ZipfsUtils.java create mode 100644 test/jdk/sap/ZipfsUtilsTest.java create mode 100644 test/jdk/sap/zip-with-symlink.zip diff --git a/src/java.base/share/classes/jdk/internal/access/JdkNioZipfsAccess.java b/src/java.base/share/classes/jdk/internal/access/JdkNioZipfsAccess.java new file mode 100644 index 000000000000..77bf4eb3bdbd --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/access/JdkNioZipfsAccess.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.access; + +/** + * SharedSecrets interface used for the access from java.text.Bidi + */ + +public interface JdkNioZipfsAccess { + + // java.awt.font.TextAttribute constants + public boolean isSymbolicLink(Object path); +} diff --git a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java index 4d70095b157a..c02f76f28a91 100644 --- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java +++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java @@ -109,6 +109,8 @@ public class SharedSecrets { @Stable private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess; @Stable private static JavaxCryptoSpecAccess javaxCryptoSpecAccess; @Stable private static JavaxSecurityAccess javaxSecurityAccess; + // SapMachine 2026-04-14: Support for symlink detection in zipfs. + @Stable private static JdkNioZipfsAccess jdkNioZipfsAccess; public static void setJavaUtilCollectionAccess(JavaUtilCollectionAccess juca) { javaUtilCollectionAccess = juca; @@ -537,4 +539,14 @@ private static void ensureClassInitialized(Class c) { MethodHandles.lookup().ensureInitialized(c); } catch (IllegalAccessException e) {} } + + // SapMachine 2026-04-14 + public static void setJdkNioZipfsAccess(JdkNioZipfsAccess access) { + jdkNioZipfsAccess = access; + } + + // SapMachine 2026-04-14 + public static JdkNioZipfsAccess getJdkNioZipfsAccess() { + return jdkNioZipfsAccess; + } } diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index a16a66066074..e1de25572ed7 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -167,6 +167,8 @@ jdk.net, // SapMachine 2024-06-12: process group extension jdk.sapext, + // SapMachine 2026-04-13: symlink support for zipfs entries in sapext. + jdk.zipfs, jdk.sctp, jdk.crypto.cryptoki; exports jdk.internal.classfile.components to diff --git a/src/jdk.sapext/share/classes/com/sap/jdk/ext/util/ZipfsUtils.java b/src/jdk.sapext/share/classes/com/sap/jdk/ext/util/ZipfsUtils.java new file mode 100644 index 000000000000..ce81bd487ac7 --- /dev/null +++ b/src/jdk.sapext/share/classes/com/sap/jdk/ext/util/ZipfsUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + package com.sap.jdk.ext.util; + + import jdk.internal.access.JdkNioZipfsAccess; + import jdk.internal.access.SharedSecrets; + + public class ZipfsUtils { + + // Silence warning about implicit ctor. + private ZipfsUtils() { + } + + /** + * Returns true if the given path is a {@link jdk.nio.zipfs.ZipPath} which + * represents a symbolic link. + * + * @param path The path in the zipfs. + * @return true if the path represents a symbolic link. + */ + public static boolean isSymbolicLink(Object path) { + JdkNioZipfsAccess access = SharedSecrets.getJdkNioZipfsAccess(); + + if (access == null) { + return false; + } + + return access.isSymbolicLink(path); + } + } \ No newline at end of file diff --git a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java index 3223ff9dccd4..e8421bddccba 100644 --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java @@ -3645,4 +3645,17 @@ public boolean equals(Object other) { oname, 0, oname.length); } } + + // SapMachine 2026-04-14: Returns true if the resolved path points to a symnbolic link. + boolean isSymlink(byte[] path) { + IndexNode inode = getInode(path); + + if ((inode != null) && (inode.pos != -1)) { + long attrEx = ZipConstants.CENATX(cen, inode.pos); + + return (attrEx & 0xF0000000L) == 0xA0000000L; + } + + return false; + } } diff --git a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java index adfa975c1c37..8dbd3fcac9a9 100644 --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java @@ -62,6 +62,25 @@ final class ZipPath implements Path { private volatile int[] offsets; private int hashcode = 0; // cached hashcode (created lazily) + // SapMachine 2026-04-14: Support for symlink detectiun. + static class JdkNioZipfsAccessImpl implements jdk.internal.access.JdkNioZipfsAccess { + public boolean isSymbolicLink(Object path) { + if (!(path instanceof ZipPath)) { + return false; + } + + ZipPath zipPath = (ZipPath) path; + byte[] resolvedPath = zipPath.getResolvedPath(); + + return zipPath.zfs.isSymlink(resolvedPath); + } + } + + // SapMachine 2026-04-14: Support for symlink detectiun. + static { + jdk.internal.access.SharedSecrets.setJdkNioZipfsAccess(new JdkNioZipfsAccessImpl()); + } + ZipPath(ZipFileSystem zfs, byte[] path) { this(zfs, path, false); } diff --git a/test/jdk/sap/ZipfsUtilsTest.java b/test/jdk/sap/ZipfsUtilsTest.java new file mode 100644 index 000000000000..909346ffafb4 --- /dev/null +++ b/test/jdk/sap/ZipfsUtilsTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/** + * @test + * @summary Runs the test for com.sap.jdk.ext.util.ZipfsUtils. + * + * @run junit ZipfsUtilsTest + */ + +import java.io.File; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.util.Map; + +import com.sap.jdk.ext.util.ZipfsUtils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ZipfsUtilsTest { + + @Test + public void basicTest() throws Exception { + File file = new File(System.getProperty("test.src", "."), "zip-with-symlink.zip"); + URI uri = new URI("jar", file.toURI().toString(), null); + FileSystem fs = FileSystems.newFileSystem(uri, Map.of()); + assertFalse(ZipfsUtils.isSymbolicLink(fs.getPath("file"))); + assertTrue(ZipfsUtils.isSymbolicLink(fs.getPath("symlink"))); + } +} diff --git a/test/jdk/sap/zip-with-symlink.zip b/test/jdk/sap/zip-with-symlink.zip new file mode 100644 index 0000000000000000000000000000000000000000..98c94ff89cf30c4fa96103af1732377a2133363b GIT binary patch literal 306 zcmWIWW@h1H00GUMz8EkAO0Y1ezB(%jt20B=SnIc8kWkbt{t yOCyL0aTF`WQE28Nn}q2kgh~Gw+5t_1I}m6X7RLp6vx2 Date: Mon, 20 Apr 2026 15:38:08 +0200 Subject: [PATCH 2/2] Fix whitespace and use Path instead of Object. --- .../jdk/internal/access/JdkNioZipfsAccess.java | 4 +++- .../classes/com/sap/jdk/ext/util/ZipfsUtils.java | 16 +++++++++------- .../share/classes/jdk/nio/zipfs/ZipPath.java | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/access/JdkNioZipfsAccess.java b/src/java.base/share/classes/jdk/internal/access/JdkNioZipfsAccess.java index 77bf4eb3bdbd..a7789a4beef5 100644 --- a/src/java.base/share/classes/jdk/internal/access/JdkNioZipfsAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JdkNioZipfsAccess.java @@ -25,6 +25,8 @@ package jdk.internal.access; +import java.nio.file.Path; + /** * SharedSecrets interface used for the access from java.text.Bidi */ @@ -32,5 +34,5 @@ public interface JdkNioZipfsAccess { // java.awt.font.TextAttribute constants - public boolean isSymbolicLink(Object path); + public boolean isSymbolicLink(Path path); } diff --git a/src/jdk.sapext/share/classes/com/sap/jdk/ext/util/ZipfsUtils.java b/src/jdk.sapext/share/classes/com/sap/jdk/ext/util/ZipfsUtils.java index ce81bd487ac7..0c2ef76b28e9 100644 --- a/src/jdk.sapext/share/classes/com/sap/jdk/ext/util/ZipfsUtils.java +++ b/src/jdk.sapext/share/classes/com/sap/jdk/ext/util/ZipfsUtils.java @@ -24,10 +24,12 @@ */ package com.sap.jdk.ext.util; - import jdk.internal.access.JdkNioZipfsAccess; - import jdk.internal.access.SharedSecrets; +import java.nio.file.Path; + +import jdk.internal.access.JdkNioZipfsAccess; +import jdk.internal.access.SharedSecrets; - public class ZipfsUtils { +public class ZipfsUtils { // Silence warning about implicit ctor. private ZipfsUtils() { @@ -40,13 +42,13 @@ private ZipfsUtils() { * @param path The path in the zipfs. * @return true if the path represents a symbolic link. */ - public static boolean isSymbolicLink(Object path) { + public static boolean isSymbolicLink(Path path) { JdkNioZipfsAccess access = SharedSecrets.getJdkNioZipfsAccess(); if (access == null) { return false; - } + } - return access.isSymbolicLink(path); + return access.isSymbolicLink(path); } - } \ No newline at end of file +} diff --git a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java index 8dbd3fcac9a9..161611f61c56 100644 --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java @@ -64,7 +64,7 @@ final class ZipPath implements Path { // SapMachine 2026-04-14: Support for symlink detectiun. static class JdkNioZipfsAccessImpl implements jdk.internal.access.JdkNioZipfsAccess { - public boolean isSymbolicLink(Object path) { + public boolean isSymbolicLink(Path path) { if (!(path instanceof ZipPath)) { return false; }