1 /* 2 * Copyright (c) 2018, SAP SE. All rights reserved. 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact SAP SE, Dietmar-Hopp-Allee 16, 69190 Walldorf, Germany 23 * or visit www.sap.com if you need additional information or have any 24 * questions. 25 */ 26 27 import static java.nio.file.attribute.PosixFilePermission.*; 28 import static org.testng.Assert.assertEquals; 29 import static org.testng.Assert.assertNotEquals; 30 import static org.testng.Assert.assertNotNull; 31 import static org.testng.Assert.assertTrue; 32 import static org.testng.Assert.fail; 33 34 import java.io.IOException; 35 import java.nio.file.CopyOption; 36 import java.nio.file.DirectoryStream; 37 import java.nio.file.FileSystem; 38 import java.nio.file.FileVisitResult; 39 import java.nio.file.Files; 40 import java.nio.file.Path; 41 import java.nio.file.Paths; 42 import java.nio.file.SimpleFileVisitor; 43 import java.nio.file.StandardCopyOption; 44 import java.nio.file.attribute.BasicFileAttributes; 45 import java.nio.file.attribute.PosixFileAttributes; 46 import java.nio.file.attribute.PosixFileAttributeView; 47 import java.nio.file.attribute.PosixFilePermission; 48 import java.nio.file.attribute.PosixFilePermissions; 49 import java.nio.file.spi.FileSystemProvider; 50 import java.util.Collections; 51 import java.util.HashMap; 52 import java.util.Map; 53 import java.util.Set; 54 import java.util.concurrent.atomic.AtomicInteger; 55 56 import org.testng.annotations.Test; 57 58 /** 59 * @test 60 * @modules jdk.zipfs 61 * @run testng TestPosixPerms 62 * @summary Test zip file operations handling POSIX permissions. 63 */ 64 public class TestPosixPerms { 65 66 private static final String ZIP_FILE = "testPosixPerms.zip"; 67 private static final String ZIP_FILE_COPY = "testPosixPermsCopy.zip"; 68 private static final String ZIP_FILE_NEW = "testPosixPermsNew.zip"; 69 private static final String UNZIP_DIR = "unzip/"; 70 private static final Map<String, Object> CREATE_TRUE = new HashMap<>(); 71 private static final FileSystemProvider zipFS; 72 private static final CopyOption[] NO_OPTIONS = {}; 73 private static final CopyOption[] COPY_ATTRIBUTES = {StandardCopyOption.COPY_ATTRIBUTES}; 74 75 public static class CopyVisitor extends SimpleFileVisitor<Path> { 76 private Path from, to; 77 private final CopyOption[] options; 78 79 public CopyVisitor(Path from, Path to, boolean manualCopyPosixPerms, CopyOption... options) { 80 this.from = from; 81 this.to = to; 82 this.options = manualCopyPosixPerms ? NO_OPTIONS : COPY_ATTRIBUTES; 83 } 84 85 private static void copyPermissions(Path source, Path target) throws IOException { 86 PosixFileAttributeView targetAttrs; 87 if ((targetAttrs = Files.getFileAttributeView(target, PosixFileAttributeView.class)) != null) { 88 try { 89 PosixFileAttributes sourceAttrs = Files.readAttributes(source, PosixFileAttributes.class); 90 if (sourceAttrs != null) { 91 targetAttrs.setPermissions(sourceAttrs.permissions()); 92 } 93 } catch (UnsupportedOperationException uoe) { 94 // Just ignore if source has no Posix file permissions. 95 } 96 } 97 } 98 99 @Override 100 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 101 Path target = to.resolve(from.relativize(dir).toString()); 102 if (!Files.exists(target)) { 103 Files.copy(dir, target, options); 104 if (options == NO_OPTIONS) { 105 copyPermissions(dir, target); 106 } 107 } 108 return FileVisitResult.CONTINUE; 109 } 110 111 @Override 112 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 113 Path target = to.resolve(from.relativize(file).toString()); 114 Files.copy(file, target, options); 115 if (options == NO_OPTIONS) { 116 copyPermissions(file, target); 117 } 118 return FileVisitResult.CONTINUE; 119 } 120 } 121 122 private int entriesCreated; 123 124 static { 125 zipFS = getZipFSProvider(); 126 assertNotNull(zipFS, "ZIP filesystem provider is not installed"); 127 CREATE_TRUE.put("create", "true"); 128 } 129 130 private static FileSystemProvider getZipFSProvider() { 131 for (FileSystemProvider provider : FileSystemProvider.installedProviders()) { 132 if ("jar".equals(provider.getScheme())) 133 return provider; 134 } 135 return null; 136 } 137 138 private void checkPermissionsOfEntry(Path file, boolean directory, Set<PosixFilePermission> expected) { 139 System.out.println("Checking " + file + "..."); 140 assertEquals(Files.isDirectory(file), directory, "Unexpected directory attribute."); 141 try { 142 System.out.println(Files.readAttributes(file, PosixFileAttributes.class).toString()); 143 } catch (IOException e) { 144 fail("Failed to list file attributes (posix) for entry.", e); 145 } 146 try { 147 Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(file); 148 assertNotEquals(permissions, null, "Retruned permissions of entry are null. " + 149 "This should not happen but we should rather see an UnsupportedOperationException."); 150 assertNotEquals(expected, null, "Got a set of " + permissions.size() + 151 " permissions but expected null/UnsupportedOperationException."); 152 assertEquals(permissions.size(), expected.size(), "Unexpected number of permissions( " + 153 permissions.size() + " received vs " + expected.size() + " expected)."); 154 for (PosixFilePermission p : expected) { 155 assertTrue(permissions.contains(p), "Posix permission " + p + " missing."); 156 } 157 } catch (UnsupportedOperationException e) { 158 if (expected != null) { 159 fail("Unexpected: No posix permissions associated with entry."); 160 } 161 } catch (IOException e) { 162 fail("Caught unexpected exception obtaining posix file permissions.", e); 163 } 164 } 165 166 private void putFile(FileSystem fs, String name, Set<PosixFilePermission> perms) throws IOException { 167 if (perms == null) { 168 Files.createFile(fs.getPath(name)); 169 } else { 170 Files.createFile(fs.getPath(name), PosixFilePermissions.asFileAttribute(perms)); 171 } 172 entriesCreated++; 173 } 174 175 private void putDirectory(FileSystem fs, String name, Set<PosixFilePermission> perms) throws IOException { 176 if (perms == null) { 177 Files.createDirectory(fs.getPath(name)); 178 } else { 179 Files.createDirectory(fs.getPath(name), PosixFilePermissions.asFileAttribute(perms)); 180 } 181 entriesCreated++; 182 } 183 184 private FileSystem openOrcreateZipFile(Path zpath) throws Exception { 185 if (Files.exists(zpath)) { 186 FileSystem fs = zipFS.newFileSystem(zpath, new HashMap<>()); 187 return fs; 188 } else { 189 System.out.println("Create " + zpath + "..."); 190 FileSystem fs = zipFS.newFileSystem(zpath, CREATE_TRUE); 191 putDirectory(fs, "dir", Set.of( 192 OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, 193 GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, 194 OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE)); 195 putFile(fs, "uread", Set.of(OWNER_READ)); 196 putFile(fs, "uwrite", Set.of(OWNER_WRITE)); 197 putFile(fs, "uexec", Set.of(OWNER_EXECUTE)); 198 putFile(fs, "gread", Set.of(GROUP_READ)); 199 putFile(fs, "gwrite", Set.of(GROUP_WRITE)); 200 putFile(fs, "gexec", Set.of(GROUP_EXECUTE)); 201 putFile(fs, "oread", Set.of(OTHERS_READ)); 202 putFile(fs, "owrite", Set.of(OTHERS_WRITE)); 203 putFile(fs, "oexec", Set.of(OTHERS_EXECUTE)); 204 putFile(fs, "emptyperms", Collections.<PosixFilePermission>emptySet()); 205 putFile(fs, "noperms", null); 206 putFile(fs, "permsaddedlater", null); 207 Files.setPosixFilePermissions(fs.getPath("permsaddedlater"), Set.of(OWNER_READ)); 208 return fs; 209 } 210 } 211 212 private void checkPosixPerms(Path path) throws Exception { 213 AtomicInteger entries = new AtomicInteger(); 214 215 try (DirectoryStream<Path> paths = Files.newDirectoryStream(path)) { 216 paths.forEach(file -> { 217 entries.getAndIncrement(); 218 String name = file.getFileName().toString(); 219 if (name.startsWith("dir")) { 220 checkPermissionsOfEntry(file, true, Set.of( 221 OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, 222 GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, 223 OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE)); 224 } else if (name.equals("uread")) { 225 checkPermissionsOfEntry(file, false, Set.of(OWNER_READ)); 226 } else if (name.equals("uwrite")) { 227 checkPermissionsOfEntry(file, false, Set.of(OWNER_WRITE)); 228 } else if (name.equals("uexec")) { 229 checkPermissionsOfEntry(file, false, Set.of(OWNER_EXECUTE)); 230 } else if (name.equals("gread")) { 231 checkPermissionsOfEntry(file, false, Set.of(GROUP_READ)); 232 } else if (name.equals("gwrite")) { 233 checkPermissionsOfEntry(file, false, Set.of(GROUP_WRITE)); 234 } else if (name.equals("gexec")) { 235 checkPermissionsOfEntry(file, false, Set.of(GROUP_EXECUTE)); 236 } else if (name.equals("oread")) { 237 checkPermissionsOfEntry(file, false, Set.of(OTHERS_READ)); 238 } else if (name.equals("owrite")) { 239 checkPermissionsOfEntry(file, false, Set.of(OTHERS_WRITE)); 240 } else if (name.equals("oexec")) { 241 checkPermissionsOfEntry(file, false, Set.of(OTHERS_EXECUTE)); 242 } else if (name.equals("emptyperms")) { 243 checkPermissionsOfEntry(file, false, Collections.<PosixFilePermission>emptySet()); 244 } else if (name.equals("noperms")) { 245 // Only check for "no permissions" in files in the zip file system 246 // If such a file gets extracted into a "normal" file system it will 247 // get the system default permissions (i.e. "umask") 248 if ("jar".equals(path.getFileSystem().provider().getScheme())) { 249 checkPermissionsOfEntry(file, false, null); 250 } 251 } else if (name.equals("permsaddedlater")) { 252 checkPermissionsOfEntry(file, false, Set.of(OWNER_READ)); 253 } else { 254 fail("Found unknown entry " + name + "."); 255 } 256 }); 257 } 258 System.out.println("Number of entries: " + entries.get() + "."); 259 assertEquals(entries.get(), entriesCreated, "File contained wrong number of entries."); 260 } 261 262 @Test(priority=0) 263 public void testWriteAndReadArchiveWithPosixPerms() throws Exception { 264 Path in = Paths.get(System.getProperty("test.dir", "."), ZIP_FILE); 265 266 try (FileSystem zipIn = openOrcreateZipFile(in)) { 267 System.out.println("Test reading " + in + "..."); 268 checkPosixPerms(zipIn.getPath("/")); 269 } 270 } 271 272 @Test(priority=1) 273 public void testPosixPermsAfterCopy() throws Exception { 274 Path in = Paths.get(System.getProperty("test.dir", "."), ZIP_FILE); 275 Path out = Paths.get(System.getProperty("test.dir", "."), ZIP_FILE_COPY); 276 277 try (FileSystem zipIn = openOrcreateZipFile(in); 278 FileSystem zipOut = zipFS.newFileSystem(out, CREATE_TRUE)) { 279 Path from = zipIn.getPath("/"); 280 Path to = zipOut.getPath("/"); 281 Files.walkFileTree(from, new CopyVisitor(from, to, false, StandardCopyOption.COPY_ATTRIBUTES)); 282 System.out.println("Test reading " + out + "..."); 283 checkPosixPerms(to); 284 } 285 } 286 287 @Test(priority=2) 288 public void testPosixPermsAfterUnzip() throws Exception { 289 Path in = Paths.get(System.getProperty("test.dir", "."), ZIP_FILE); 290 Path out = Files.createDirectory(Paths.get(System.getProperty("test.dir", "."), UNZIP_DIR)); 291 boolean posixFS; 292 try { 293 Files.getPosixFilePermissions(out); 294 posixFS = true; 295 } catch (UnsupportedOperationException e) { 296 posixFS = false; 297 } 298 299 try (FileSystem zipIn = openOrcreateZipFile(in)) { 300 Path from = zipIn.getPath("/"); 301 Files.walkFileTree(from, new CopyVisitor(from, out, true, StandardCopyOption.COPY_ATTRIBUTES)); 302 System.out.println("Test reading " + out + "..."); 303 if (posixFS) { 304 checkPosixPerms(out); 305 } 306 } 307 } 308 309 @Test(priority=3) 310 public void testPosixPermsAfterZip() throws Exception { 311 Path in = Paths.get(System.getProperty("test.dir", "."), UNZIP_DIR); 312 Path out = Paths.get(System.getProperty("test.dir", "."), ZIP_FILE_NEW); 313 boolean posixFS; 314 try { 315 Files.getPosixFilePermissions(in); 316 posixFS = true; 317 } catch (UnsupportedOperationException e) { 318 posixFS = false; 319 } 320 321 try (FileSystem zipOut = zipFS.newFileSystem(out, CREATE_TRUE)) { 322 out = zipOut.getPath("/"); 323 324 // Have to hack this manually otherwise we won't be able to read these files at all 325 Set<PosixFilePermission> emptyperms_p = null, gexec_p = null, gwrite_p = null, gread_p = null, 326 oread_p = null, oexec_p = null, owrite_p = null, uexec_p = null, uwrite_p = null; 327 if (posixFS) { 328 Path emptyperms = in.resolve("emptyperms"); 329 emptyperms_p = Files.getPosixFilePermissions(emptyperms); 330 emptyperms_p.add(OWNER_READ); 331 Files.setPosixFilePermissions(emptyperms, emptyperms_p); 332 Path gexec = in.resolve("gexec"); 333 gexec_p = Files.getPosixFilePermissions(gexec); 334 gexec_p.add(OWNER_READ); 335 Files.setPosixFilePermissions(gexec, gexec_p); 336 Path gwrite = in.resolve("gwrite"); 337 gwrite_p = Files.getPosixFilePermissions(gwrite); 338 gwrite_p.add(OWNER_READ); 339 Files.setPosixFilePermissions(gwrite, gwrite_p); 340 Path gread = in.resolve("gread"); 341 gread_p = Files.getPosixFilePermissions(gread); 342 gread_p.add(OWNER_READ); 343 Files.setPosixFilePermissions(gread, gread_p); 344 Path oread = in.resolve("oread"); 345 oread_p = Files.getPosixFilePermissions(oread); 346 oread_p.add(OWNER_READ); 347 Files.setPosixFilePermissions(oread, oread_p); 348 Path oexec = in.resolve("oexec"); 349 oexec_p = Files.getPosixFilePermissions(oexec); 350 oexec_p.add(OWNER_READ); 351 Files.setPosixFilePermissions(oexec, oexec_p); 352 Path owrite = in.resolve("owrite"); 353 owrite_p = Files.getPosixFilePermissions(owrite); 354 owrite_p.add(OWNER_READ); 355 Files.setPosixFilePermissions(owrite, owrite_p); 356 Path uexec = in.resolve("uexec"); 357 uexec_p = Files.getPosixFilePermissions(uexec); 358 uexec_p.add(OWNER_READ); 359 Files.setPosixFilePermissions(uexec, uexec_p); 360 Path uwrite = in.resolve("uwrite"); 361 uwrite_p = Files.getPosixFilePermissions(uwrite); 362 uwrite_p.add(OWNER_READ); 363 Files.setPosixFilePermissions(uwrite, uwrite_p); 364 } 365 366 Files.walkFileTree(in, new CopyVisitor(in, out, true, StandardCopyOption.COPY_ATTRIBUTES)); 367 368 if (posixFS) { 369 // Fix back all the files in the target zip file which have been made readable before 370 emptyperms_p.remove(OWNER_READ); 371 Files.setPosixFilePermissions(zipOut.getPath("emptyperms"), emptyperms_p); 372 gexec_p.remove(OWNER_READ); 373 Files.setPosixFilePermissions(zipOut.getPath("gexec"), gexec_p); 374 gwrite_p.remove(OWNER_READ); 375 Files.setPosixFilePermissions(zipOut.getPath("gwrite"), gwrite_p); 376 gread_p.remove(OWNER_READ); 377 Files.setPosixFilePermissions(zipOut.getPath("gread"), gread_p); 378 oread_p.remove(OWNER_READ); 379 Files.setPosixFilePermissions(zipOut.getPath("oread"), oread_p); 380 oexec_p.remove(OWNER_READ); 381 Files.setPosixFilePermissions(zipOut.getPath("oexec"), oexec_p); 382 owrite_p.remove(OWNER_READ); 383 Files.setPosixFilePermissions(zipOut.getPath("owrite"), owrite_p); 384 uexec_p.remove(OWNER_READ); 385 Files.setPosixFilePermissions(zipOut.getPath("uexec"), uexec_p); 386 uwrite_p.remove(OWNER_READ); 387 Files.setPosixFilePermissions(zipOut.getPath("uwrite"), uwrite_p); 388 // Fix "noperms" which has the default permissions after unzipping in 'testPosixPermsAfterUnzip()' 389 Files.setPosixFilePermissions(zipOut.getPath("noperms"), null); 390 } 391 392 System.out.println("Test reading " + out + "..."); 393 if (posixFS) { 394 checkPosixPerms(out); 395 } 396 } 397 } 398 }