From 74af9c49d7e6455887a473f69ff234975b0a3851 Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Mon, 18 May 2026 17:05:45 +0200 Subject: [PATCH] permissions for archives fixed --- .../kfmanager/service/FileOperations.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java index 71ec33a..bf55aaf 100644 --- a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java +++ b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java @@ -13,11 +13,15 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.ByteArrayInputStream; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Stream; import java.util.zip.ZipEntry; @@ -1213,6 +1217,39 @@ public class FileOperations { } } + private static void applyPosixPermissions(File file, int mode) { + if (mode <= 0) return; + if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) return; + + try { + // mode for Unix is usually the last 9 bits (0777) + // But some sources might provide more (sticky bits, types, etc.) + // We focus on the standard 9-bit permission mask + int permissionMask = mode & 0777; + + Set permissions = new HashSet<>(); + + // Check OWNER (0700) + if ((permissionMask & 0400) != 0) permissions.add(PosixFilePermission.OWNER_READ); + if ((permissionMask & 0200) != 0) permissions.add(PosixFilePermission.OWNER_WRITE); + if ((permissionMask & 0100) != 0) permissions.add(PosixFilePermission.OWNER_EXECUTE); + + // Check GROUP (0070) + if ((permissionMask & 0040) != 0) permissions.add(PosixFilePermission.GROUP_READ); + if ((permissionMask & 0020) != 0) permissions.add(PosixFilePermission.GROUP_WRITE); + if ((permissionMask & 0010) != 0) permissions.add(PosixFilePermission.GROUP_EXECUTE); + + // Check OTHERS (0007) + if ((permissionMask & 0004) != 0) permissions.add(PosixFilePermission.OTHERS_READ); + if ((permissionMask & 0002) != 0) permissions.add(PosixFilePermission.OTHERS_WRITE); + if ((permissionMask & 0001) != 0) permissions.add(PosixFilePermission.OTHERS_EXECUTE); + + Files.setPosixFilePermissions(file.toPath(), permissions); + } catch (Exception e) { + // Best effort + } + } + /** * Extract ZIP/JAR/WAR by first loading archive bytes to memory and then * reading entries from ZipInputStream. @@ -1289,6 +1326,18 @@ public class FileOperations { if (callback != null && callback.isCancelled()) break; net.lingala.zip4j.model.FileHeader header = headers.get(i); zipFile.extractFile(header, targetDir.getAbsolutePath()); + + // Preserve permissions for Linux/Unix + byte[] externalAttributes = header.getExternalFileAttributes(); + if (externalAttributes != null && externalAttributes.length >= 4) { + // Unix mode is stored in the upper 16 bits of the external file attributes + int mode = ((externalAttributes[3] & 0xFF) << 8) | (externalAttributes[2] & 0xFF); + if (mode > 0) { + File extractedFile = new File(targetDir, header.getFileName()); + applyPosixPermissions(extractedFile, mode); + } + } + if (callback != null) { callback.onProgress(i + 1, total, header.getFileName()); } @@ -1429,6 +1478,15 @@ public class FileOperations { } } } + + // Preserve permissions for Linux/Unix + int mode = entry.getMode(); + if (mode > 0) { + // For TAR entries, if high bits are set (directory/file type info), + // we ensure we are looking at the standard permission bits. + applyPosixPermissions(targetFile, mode); + } + if (callback != null) { callback.onProgress(0, 0, entry.getName()); // 0/0 because we don't know total }