1 /* 2 * Copyright (c) 2018, 2019 SAP SE. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact SAP SE, Dietmar-Hopp-Allee 16, 69190 Walldorf, Germany 20 * or visit www.sap.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.File; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.nio.file.CopyOption; 29 import java.nio.file.DirectoryStream; 30 import java.nio.file.FileSystem; 31 import java.nio.file.FileVisitResult; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.nio.file.Paths; 35 import java.nio.file.SimpleFileVisitor; 36 import java.nio.file.StandardCopyOption; 37 import java.nio.file.attribute.BasicFileAttributes; 38 import java.nio.file.attribute.GroupPrincipal; 39 import java.nio.file.attribute.PosixFileAttributeView; 40 import java.nio.file.attribute.PosixFileAttributes; 41 import java.nio.file.attribute.PosixFilePermission; 42 import java.nio.file.attribute.PosixFilePermissions; 43 import java.nio.file.attribute.UserPrincipal; 44 import java.nio.file.spi.FileSystemProvider; 45 import java.security.AccessController; 46 import java.security.PrivilegedAction; 47 import java.security.PrivilegedActionException; 48 import java.security.PrivilegedExceptionAction; 49 import java.util.Collections; 50 import java.util.Enumeration; 51 import java.util.HashMap; 52 import java.util.Map; 53 import java.util.Set; 54 import java.util.concurrent.atomic.AtomicInteger; 55 import java.util.zip.ZipEntry; 56 import java.util.zip.ZipFile; 57 58 import org.testng.annotations.Test; 59 60 import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE; 61 import static java.nio.file.attribute.PosixFilePermission.GROUP_READ; 62 import static java.nio.file.attribute.PosixFilePermission.GROUP_WRITE; 63 import static java.nio.file.attribute.PosixFilePermission.OTHERS_EXECUTE; 64 import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ; 65 import static java.nio.file.attribute.PosixFilePermission.OTHERS_WRITE; 66 import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; 67 import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; 68 import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; 69 import static org.testng.Assert.assertEquals; 70 import static org.testng.Assert.assertNotNull; 71 import static org.testng.Assert.assertNull; 72 import static org.testng.Assert.assertTrue; 73 import static org.testng.Assert.fail; 74 75 /** 76 * @test 77 * @bug 8213031 78 * @modules jdk.zipfs 79 * @run testng TestPosix 80 * @run testng/othervm/java.security.policy=test.policy TestPosix 81 * @summary Test POSIX zip file operations. 82 */ 83 public class TestPosix { 84 // files and directories 85 private static final Path ZIP_FILE = Paths.get("testPosix.zip"); 86 private static final Path ZIP_FILE_COPY = Paths.get("testPosixCopy.zip"); 87 private static final Path UNZIP_DIR = Paths.get("unzip/"); 88 89 // permission sets 90 private static final Set<PosixFilePermission> ALLPERMS = 91 PosixFilePermissions.fromString("rwxrwxrwx"); 92 private static final Set<PosixFilePermission> EMPTYPERMS = 93 Collections.<PosixFilePermission>emptySet(); 94 private static final Set<PosixFilePermission> UR = Set.of(OWNER_READ); 95 private static final Set<PosixFilePermission> UW = Set.of(OWNER_WRITE); 96 private static final Set<PosixFilePermission> UE = Set.of(OWNER_EXECUTE); 97 private static final Set<PosixFilePermission> GR = Set.of(GROUP_READ); 98 private static final Set<PosixFilePermission> GW = Set.of(GROUP_WRITE); 99 private static final Set<PosixFilePermission> GE = Set.of(GROUP_EXECUTE); 100 private static final Set<PosixFilePermission> OR = Set.of(OTHERS_READ); 101 private static final Set<PosixFilePermission> OW = Set.of(OTHERS_WRITE); 102 private static final Set<PosixFilePermission> OE = Set.of(OTHERS_EXECUTE); 103 104 // principals 105 private static final UserPrincipal DUMMY_USER = ()->"defusr"; 106 private static final GroupPrincipal DUMMY_GROUP = ()->"defgrp"; 107 108 // FS open options 109 private static final Map<String, Object> ENV_DEFAULT = Collections.<String, Object>emptyMap(); 110 private static final Map<String, Object> ENV_POSIX = Map.of("enablePosixFileAttributes", true); 111 112 // misc 113 private static final FileSystemProvider zipFSP; 114 private static final CopyOption[] COPY_ATTRIBUTES = {StandardCopyOption.COPY_ATTRIBUTES}; 115 private static final Map<String, ZipFileEntryInfo> ENTRIES = new HashMap<>(); 116 117 private int entriesCreated; 118 119 static enum checkExpects { 120 contentOnly, 121 noPermDataInZip, 122 permsInZip, 123 permsPosix 124 } 125 126 static class ZipFileEntryInfo { 127 // permissions to set initially 128 private final Set<PosixFilePermission> intialPerms; 129 // permissions to set in a later call 130 private final Set<PosixFilePermission> laterPerms; 131 // permissions that should be effective in the zip file 132 private final Set<PosixFilePermission> permsInZip; 133 // permissions that should be returned by zipfs w/Posix support 134 private final Set<PosixFilePermission> permsPosix; 135 // entry is a directory 136 private final boolean isDir; 137 // need additional read flag in copy test 138 private final boolean setReadFlag; 139 140 private ZipFileEntryInfo(Set<PosixFilePermission> initialPerms, Set<PosixFilePermission> laterPerms, 141 Set<PosixFilePermission> permsInZip, Set<PosixFilePermission> permsZipPosix, boolean isDir, boolean setReadFlag) 142 { 143 this.intialPerms = initialPerms; 144 this.laterPerms = laterPerms; 145 this.permsInZip = permsInZip; 146 this.permsPosix = permsZipPosix; 147 this.isDir = isDir; 148 this.setReadFlag = setReadFlag; 149 } 150 } 151 152 static class CopyVisitor extends SimpleFileVisitor<Path> { 153 private Path from, to; 154 private boolean copyPerms; 155 156 CopyVisitor(Path from, Path to) { 157 this.from = from; 158 this.to = to; 159 } 160 161 CopyVisitor(Path from, Path to, boolean copyPerms) { 162 this.from = from; 163 this.to = to; 164 this.copyPerms = copyPerms; 165 } 166 167 @Override 168 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 169 FileVisitResult rc = super.preVisitDirectory(dir, attrs); 170 Path target = to.resolve(from.relativize(dir).toString()); 171 if (!Files.exists(target)) { 172 Files.copy(dir, target, COPY_ATTRIBUTES); 173 if (copyPerms) { 174 Files.setPosixFilePermissions(target, Files.getPosixFilePermissions(dir)); 175 } 176 } 177 return rc; 178 } 179 180 @Override 181 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 182 FileVisitResult rc = super.visitFile(file, attrs); 183 Path target = to.resolve(from.relativize(file).toString()); 184 Files.copy(file, target, COPY_ATTRIBUTES); 185 if (copyPerms) { 186 Files.setPosixFilePermissions(target, Files.getPosixFilePermissions(file)); 187 } 188 return rc; 189 } 190 } 191 192 static class DeleteVisitor extends SimpleFileVisitor<Path> { 193 @Override 194 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 195 FileVisitResult rc = super.postVisitDirectory(dir, exc); 196 Files.delete(dir); 197 return rc; 198 } 199 200 @Override 201 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 202 FileVisitResult rc = super.visitFile(file, attrs); 203 Files.delete(file); 204 return rc; 205 } 206 } 207 208 @FunctionalInterface 209 static interface Executor { 210 void doIt() throws IOException; 211 } 212 213 static { 214 zipFSP = getZipFSProvider(); 215 assertNotNull(zipFSP, "ZIP filesystem provider is not installed"); 216 ENTRIES.put("dir", new ZipFileEntryInfo(ALLPERMS, null, ALLPERMS, ALLPERMS, true, false)); 217 ENTRIES.put("uread", new ZipFileEntryInfo(UR, null, UR, UR, false, false)); 218 ENTRIES.put("uwrite", new ZipFileEntryInfo(UW, null, UW, UW, false, true)); 219 ENTRIES.put("uexec", new ZipFileEntryInfo(UE, null, UE, UE, false, true)); 220 ENTRIES.put("gread", new ZipFileEntryInfo(GR, null, GR, GR, false, true)); 221 ENTRIES.put("gwrite", new ZipFileEntryInfo(GW, null, GW, GW, false, true)); 222 ENTRIES.put("gexec", new ZipFileEntryInfo(GE, null, GE, GE, false, true)); 223 ENTRIES.put("oread", new ZipFileEntryInfo(OR, null, OR, OR, false, true)); 224 ENTRIES.put("owrite", new ZipFileEntryInfo(OW, null, OW, OW, false, true)); 225 ENTRIES.put("oexec", new ZipFileEntryInfo(OE, null, OE, OE, false, true)); 226 ENTRIES.put("emptyperms", new ZipFileEntryInfo(EMPTYPERMS, null, EMPTYPERMS, EMPTYPERMS, false, true)); 227 ENTRIES.put("noperms", new ZipFileEntryInfo(null, null, null, ALLPERMS, false, false)); 228 ENTRIES.put("permslater", new ZipFileEntryInfo(null, UR, UR, UR, false, false)); 229 } 230 231 private static String expectedDefaultOwner(Path zf) { 232 try { 233 try { 234 PrivilegedExceptionAction<String> pa = ()->Files.getOwner(zf).getName(); 235 return AccessController.doPrivileged(pa); 236 } catch (UnsupportedOperationException e) { 237 // if we can't get the owner of the file, we fall back to system property user.name 238 PrivilegedAction<String> pa = ()->System.getProperty("user.name"); 239 return AccessController.doPrivileged(pa); 240 } 241 } catch (PrivilegedActionException | SecurityException e) { 242 System.out.println("Caught " + e.getClass().getName() + "(" + e.getMessage() + 243 ") when running a privileged operation to get the default owner."); 244 return null; 245 } 246 } 247 248 private static String expectedDefaultGroup(Path zf, String defaultOwner) { 249 try { 250 try { 251 PosixFileAttributeView zfpv = Files.getFileAttributeView(zf, PosixFileAttributeView.class); 252 if (zfpv == null) { 253 return defaultOwner; 254 } 255 PrivilegedExceptionAction<String> pa = ()->zfpv.readAttributes().group().getName(); 256 return AccessController.doPrivileged(pa); 257 } catch (UnsupportedOperationException e) { 258 return defaultOwner; 259 } 260 } catch (PrivilegedActionException | SecurityException e) { 261 System.out.println("Caught an exception when running a privileged operation to get the default group."); 262 e.printStackTrace(); 263 return null; 264 } 265 } 266 267 private static FileSystemProvider getZipFSProvider() { 268 for (FileSystemProvider provider : FileSystemProvider.installedProviders()) { 269 if ("jar".equals(provider.getScheme())) 270 return provider; 271 } 272 return null; 273 } 274 275 private void putEntry(FileSystem fs, String name, ZipFileEntryInfo entry) throws IOException { 276 if (entry.isDir) { 277 if (entry.intialPerms == null) { 278 Files.createDirectory(fs.getPath(name)); 279 } else { 280 Files.createDirectory(fs.getPath(name), PosixFilePermissions.asFileAttribute(entry.intialPerms)); 281 } 282 283 } else { 284 if (entry.intialPerms == null) { 285 Files.createFile(fs.getPath(name)); 286 } else { 287 Files.createFile(fs.getPath(name), PosixFilePermissions.asFileAttribute(entry.intialPerms)); 288 } 289 } 290 if (entry.laterPerms != null) { 291 Files.setAttribute(fs.getPath(name), "zip:permissions", entry.laterPerms); 292 } 293 entriesCreated++; 294 } 295 296 private FileSystem createTestZipFile(Path zpath, Map<String, Object> env) throws IOException { 297 if (Files.exists(zpath)) { 298 System.out.println("Deleting old " + zpath + "..."); 299 Files.delete(zpath); 300 } 301 System.out.println("Creating " + zpath + "..."); 302 entriesCreated = 0; 303 var opts = new HashMap<String, Object>(); 304 opts.putAll(env); 305 opts.put("create", true); 306 FileSystem fs = zipFSP.newFileSystem(zpath, opts); 307 for (String name : ENTRIES.keySet()) { 308 putEntry(fs, name, ENTRIES.get(name)); 309 } 310 return fs; 311 } 312 313 private FileSystem createEmptyZipFile(Path zpath, Map<String, Object> env) throws IOException { 314 if (Files.exists(zpath)) { 315 System.out.println("Deleting old " + zpath + "..."); 316 Files.delete(zpath); 317 } 318 System.out.println("Creating " + zpath + "..."); 319 var opts = new HashMap<String, Object>(); 320 opts.putAll(env); 321 opts.put("create", true); 322 return zipFSP.newFileSystem(zpath, opts); 323 } 324 325 private void delTree(Path p) throws IOException { 326 if (Files.exists(p)) { 327 Files.walkFileTree(p, new DeleteVisitor()); 328 } 329 } 330 331 private void addOwnerRead(Path root) throws IOException { 332 for (String name : ENTRIES.keySet()) { 333 ZipFileEntryInfo ei = ENTRIES.get(name); 334 if (!ei.setReadFlag) { 335 continue; 336 } 337 Path setReadOn = root.resolve(name); 338 Set<PosixFilePermission> perms = Files.getPosixFilePermissions(setReadOn); 339 perms.add(OWNER_READ); 340 Files.setPosixFilePermissions(setReadOn, perms); 341 } 342 } 343 344 private void removeOwnerRead(Path root) throws IOException { 345 for (String name : ENTRIES.keySet()) { 346 ZipFileEntryInfo ei = ENTRIES.get(name); 347 if (!ei.setReadFlag) { 348 continue; 349 } 350 Path removeReadFrom = root.resolve(name); 351 Set<PosixFilePermission> perms = Files.getPosixFilePermissions(removeReadFrom); 352 perms.remove(OWNER_READ); 353 Files.setPosixFilePermissions(removeReadFrom, perms); 354 } 355 } 356 357 @SuppressWarnings("unchecked") 358 private void checkEntry(Path file, checkExpects expected) { 359 System.out.println("Checking " + file + "..."); 360 String name = file.getFileName().toString(); 361 ZipFileEntryInfo ei = ENTRIES.get(name); 362 assertNotNull(ei, "Found unknown entry " + name + "."); 363 BasicFileAttributes attrs = null; 364 if (expected == checkExpects.permsPosix) { 365 try { 366 attrs = Files.readAttributes(file, PosixFileAttributes.class); 367 } catch (IOException e) { 368 e.printStackTrace(); 369 fail("Caught IOException reading file attributes (posix) for " + name + ": " + e.getMessage()); 370 } 371 } else { 372 try { 373 attrs = Files.readAttributes(file, BasicFileAttributes.class); 374 } catch (IOException e) { 375 e.printStackTrace(); 376 fail("Caught IOException reading file attributes (basic) " + name + ": " + e.getMessage()); 377 } 378 } 379 assertEquals(Files.isDirectory(file), ei.isDir, "Unexpected directory attribute for:" + System.lineSeparator() + attrs); 380 381 if (expected == checkExpects.contentOnly) { 382 return; 383 } 384 385 Set<PosixFilePermission> permissions; 386 if (expected == checkExpects.permsPosix) { 387 try { 388 permissions = Files.getPosixFilePermissions(file); 389 } catch (IOException e) { 390 e.printStackTrace(); 391 fail("Caught IOException getting permission attribute for:" + System.lineSeparator() + attrs); 392 return; 393 } 394 comparePermissions(ei.permsPosix, permissions); 395 } else if (expected == checkExpects.permsInZip || expected == checkExpects.noPermDataInZip) { 396 try { 397 permissions = (Set<PosixFilePermission>)Files.getAttribute(file, "zip:permissions"); 398 } catch (IOException e) { 399 e.printStackTrace(); 400 fail("Caught IOException getting permission attribute for:" + System.lineSeparator() + attrs); 401 return; 402 } 403 comparePermissions(expected == checkExpects.noPermDataInZip ? null : ei.permsInZip, permissions); 404 } 405 } 406 407 private void doCheckEntries(Path path, checkExpects expected) throws IOException { 408 AtomicInteger entries = new AtomicInteger(); 409 410 try (DirectoryStream<Path> paths = Files.newDirectoryStream(path)) { 411 paths.forEach(file -> { 412 entries.getAndIncrement(); 413 checkEntry(file, expected); 414 }); 415 } 416 System.out.println("Number of entries: " + entries.get() + "."); 417 assertEquals(entries.get(), entriesCreated, "File contained wrong number of entries."); 418 } 419 420 private void checkEntries(FileSystem fs, checkExpects expected) throws IOException { 421 System.out.println("Checking permissions on file system " + fs + "..."); 422 doCheckEntries(fs.getPath("/"), expected); 423 } 424 425 private void checkEntries(Path path, checkExpects expected) throws IOException { 426 System.out.println("Checking permissions on path " + path + "..."); 427 doCheckEntries(path, expected); 428 } 429 430 private boolean throwsUOE(Executor e) throws IOException { 431 try { 432 e.doIt(); 433 return false; 434 } catch (UnsupportedOperationException exc) { 435 return true; 436 } 437 } 438 439 private void comparePermissions(Set<PosixFilePermission> expected, Set<PosixFilePermission> actual) { 440 if (expected == null) { 441 assertNull(actual, "Permissions are not null"); 442 } else { 443 assertNotNull(actual, "Permissions are null."); 444 assertEquals(actual.size(), expected.size(), "Unexpected number of permissions (" + 445 actual.size() + " received vs " + expected.size() + " expected)."); 446 for (PosixFilePermission p : expected) { 447 assertTrue(actual.contains(p), "Posix permission " + p + " missing."); 448 } 449 } 450 } 451 452 /** 453 * This tests whether the entries in a zip file created w/o 454 * Posix support are correct. 455 * 456 * @throws IOException 457 */ 458 @Test 459 public void testDefault() throws IOException { 460 // create zip file using zipfs with default options 461 createTestZipFile(ZIP_FILE, ENV_DEFAULT).close(); 462 // check entries on zipfs with default options 463 try (FileSystem zip = zipFSP.newFileSystem(ZIP_FILE, ENV_DEFAULT)) { 464 checkEntries(zip, checkExpects.permsInZip); 465 } 466 // check entries on zipfs with posix options 467 try (FileSystem zip = zipFSP.newFileSystem(ZIP_FILE, ENV_POSIX)) { 468 checkEntries(zip, checkExpects.permsPosix); 469 } 470 } 471 472 /** 473 * This tests whether the entries in a zip file created w/ 474 * Posix support are correct. 475 * 476 * @throws IOException 477 */ 478 @Test 479 public void testPosix() throws IOException { 480 // create zip file using zipfs with posix option 481 createTestZipFile(ZIP_FILE, ENV_POSIX).close(); 482 // check entries on zipfs with default options 483 try (FileSystem zip = zipFSP.newFileSystem(ZIP_FILE, ENV_DEFAULT)) { 484 checkEntries(zip, checkExpects.permsInZip); 485 } 486 // check entries on zipfs with posix options 487 try (FileSystem zip = zipFSP.newFileSystem(ZIP_FILE, ENV_POSIX)) { 488 checkEntries(zip, checkExpects.permsPosix); 489 } 490 } 491 492 /** 493 * This tests whether the entries in a zip file copied from another 494 * are correct. 495 * 496 * @throws IOException 497 */ 498 @Test 499 public void testCopy() throws IOException { 500 // copy zip to zip with default options 501 try (FileSystem zipIn = createTestZipFile(ZIP_FILE, ENV_DEFAULT); 502 FileSystem zipOut = createEmptyZipFile(ZIP_FILE_COPY, ENV_DEFAULT)) { 503 Path from = zipIn.getPath("/"); 504 Files.walkFileTree(from, new CopyVisitor(from, zipOut.getPath("/"))); 505 } 506 // check entries on copied zipfs with default options 507 try (FileSystem zip = zipFSP.newFileSystem(ZIP_FILE_COPY, ENV_DEFAULT)) { 508 checkEntries(zip, checkExpects.permsInZip); 509 } 510 // check entries on copied zipfs with posix options 511 try (FileSystem zip = zipFSP.newFileSystem(ZIP_FILE_COPY, ENV_POSIX)) { 512 checkEntries(zip, checkExpects.permsPosix); 513 } 514 } 515 516 /** 517 * This tests whether the entries of a zip file look correct after extraction 518 * and re-packing. When not using zipfs with Posix support, we expect the 519 * effective permissions in the resulting zip file to be empty. 520 * 521 * @throws IOException 522 */ 523 @Test 524 public void testUnzipDefault() throws IOException { 525 delTree(UNZIP_DIR); 526 Files.createDirectory(UNZIP_DIR); 527 528 try (FileSystem srcZip = createTestZipFile(ZIP_FILE, ENV_DEFAULT)) { 529 Path from = srcZip.getPath("/"); 530 Files.walkFileTree(from, new CopyVisitor(from, UNZIP_DIR)); 531 } 532 533 // we just check that the entries got extracted to file system 534 checkEntries(UNZIP_DIR, checkExpects.contentOnly); 535 536 // the target zip file is opened with Posix support 537 // but we expect no permission data to be copied using the default copy method 538 try (FileSystem tgtZip = createEmptyZipFile(ZIP_FILE_COPY, ENV_POSIX)) { 539 Files.walkFileTree(UNZIP_DIR, new CopyVisitor(UNZIP_DIR, tgtZip.getPath("/"))); 540 } 541 542 // check entries on copied zipfs - no permission data should exist 543 try (FileSystem zip = zipFSP.newFileSystem(ZIP_FILE_COPY, ENV_DEFAULT)) { 544 checkEntries(zip, checkExpects.noPermDataInZip); 545 } 546 } 547 548 /** 549 * This tests whether the entries of a zip file look correct after extraction 550 * and re-packing. If the default file system supports Posix, we test whether we 551 * correctly carry the Posix permissions. Otherwise there's not much to test in 552 * this method. 553 * 554 * @throws IOException 555 */ 556 @Test 557 public void testUnzipPosix() throws IOException { 558 delTree(UNZIP_DIR); 559 Files.createDirectory(UNZIP_DIR); 560 561 try { 562 Files.getPosixFilePermissions(UNZIP_DIR); 563 } catch (Exception e) { 564 // if we run into any exception here, be it because of the fact that the file system 565 // is not Posix or if we have insufficient security permissions, we can't do this test. 566 System.out.println("This can't be tested here because of " + e); 567 return; 568 } 569 570 try (FileSystem srcZip = createTestZipFile(ZIP_FILE, ENV_POSIX)) { 571 Path from = srcZip.getPath("/"); 572 // copy permissions as well 573 Files.walkFileTree(from, new CopyVisitor(from, UNZIP_DIR, true)); 574 } 575 576 // permissions should have been propagated to file system 577 checkEntries(UNZIP_DIR, checkExpects.permsPosix); 578 579 try (FileSystem tgtZip = createEmptyZipFile(ZIP_FILE_COPY, ENV_POSIX)) { 580 // Make some files owner readable to be able to copy them into the zipfs 581 addOwnerRead(UNZIP_DIR); 582 583 // copy permissions as well 584 Files.walkFileTree(UNZIP_DIR, new CopyVisitor(UNZIP_DIR, tgtZip.getPath("/"), true)); 585 586 // Fix back all the files in the target zip file which have been made readable before 587 removeOwnerRead(tgtZip.getPath("/")); 588 } 589 590 // check entries on copied zipfs - permission data should have been propagated 591 try (FileSystem zip = zipFSP.newFileSystem(ZIP_FILE_COPY, ENV_POSIX)) { 592 checkEntries(zip, checkExpects.permsPosix); 593 } 594 } 595 596 /** 597 * Tests POSIX default behavior. 598 * 599 * @throws IOException 600 */ 601 @Test 602 public void testPosixDefaults() throws IOException { 603 // test with posix = false, expect UnsupportedOperationException 604 try (FileSystem zipIn = createTestZipFile(ZIP_FILE, ENV_DEFAULT)) { 605 var entry = zipIn.getPath("/dir"); 606 assertTrue(throwsUOE(()->Files.getPosixFilePermissions(entry))); 607 assertTrue(throwsUOE(()->Files.setPosixFilePermissions(entry, UW))); 608 assertTrue(throwsUOE(()->Files.getOwner(entry))); 609 assertTrue(throwsUOE(()->Files.setOwner(entry, DUMMY_USER))); 610 assertTrue(throwsUOE(()->Files.getFileAttributeView(entry, PosixFileAttributeView.class))); 611 } 612 613 // test with posix = true -> default values 614 try (FileSystem zipIn = zipFSP.newFileSystem(ZIP_FILE, ENV_POSIX)) { 615 String defaultOwner = expectedDefaultOwner(ZIP_FILE); 616 String defaultGroup = expectedDefaultGroup(ZIP_FILE, defaultOwner); 617 var entry = zipIn.getPath("/noperms"); 618 comparePermissions(ALLPERMS, Files.getPosixFilePermissions(entry)); 619 var owner = Files.getOwner(entry); 620 assertNotNull(owner, "owner should not be null"); 621 if (defaultOwner != null) { 622 assertEquals(owner.getName(), defaultOwner); 623 } 624 Files.setOwner(entry, DUMMY_USER); 625 assertEquals(Files.getOwner(entry), DUMMY_USER); 626 var view = Files.getFileAttributeView(entry, PosixFileAttributeView.class); 627 var group = view.readAttributes().group(); 628 assertNotNull(group, "group must not be null"); 629 if (defaultGroup != null) { 630 assertEquals(group.getName(), defaultGroup); 631 } 632 view.setGroup(DUMMY_GROUP); 633 assertEquals(view.readAttributes().group(), DUMMY_GROUP); 634 entry = zipIn.getPath("/uexec"); 635 Files.setPosixFilePermissions(entry, GR); // will be persisted 636 comparePermissions(GR, Files.getPosixFilePermissions(entry)); 637 } 638 639 // test with posix = true + custom defaults of type String 640 try (FileSystem zipIn = zipFSP.newFileSystem(ZIP_FILE, Map.of("enablePosixFileAttributes", true, 641 "defaultOwner", "auser", "defaultGroup", "agroup", "defaultPermissions", "r--------"))) 642 { 643 var entry = zipIn.getPath("/noperms"); 644 comparePermissions(UR, Files.getPosixFilePermissions(entry)); 645 assertEquals(Files.getOwner(entry).getName(), "auser"); 646 var view = Files.getFileAttributeView(entry, PosixFileAttributeView.class); 647 assertEquals(view.readAttributes().group().getName(), "agroup"); 648 // check if the change to permissions of /uexec was persisted 649 comparePermissions(GR, Files.getPosixFilePermissions(zipIn.getPath("/uexec"))); 650 } 651 652 // test with posix = true + custom defaults as Objects 653 try (FileSystem zipIn = zipFSP.newFileSystem(ZIP_FILE, Map.of("enablePosixFileAttributes", true, 654 "defaultOwner", DUMMY_USER, "defaultGroup", DUMMY_GROUP, "defaultPermissions", UR))) 655 { 656 var entry = zipIn.getPath("/noperms"); 657 comparePermissions(UR, Files.getPosixFilePermissions(entry)); 658 assertEquals(Files.getOwner(entry), DUMMY_USER); 659 var view = Files.getFileAttributeView(entry, PosixFileAttributeView.class); 660 assertEquals(view.readAttributes().group(), DUMMY_GROUP); 661 } 662 } 663 664 /** 665 * Sanity check to test whether the zip file can be unzipped with the java.util.zip API. 666 * 667 * @throws IOException 668 */ 669 @Test 670 public void testUnzipWithJavaUtilZip() throws IOException { 671 Path testZip = Files.createTempFile(Paths.get("."), "testPosix", ".zip"); 672 createTestZipFile(testZip, ENV_DEFAULT).close(); 673 delTree(UNZIP_DIR); 674 Files.createDirectory(UNZIP_DIR); 675 File targetDir = UNZIP_DIR.toFile(); 676 try (ZipFile zf = new ZipFile(testZip.toFile())) { 677 Enumeration<? extends ZipEntry> zenum = zf.entries(); 678 while (zenum.hasMoreElements()) { 679 ZipEntry ze = zenum.nextElement(); 680 File target = new File(targetDir + File.separator + ze.getName()); 681 if (ze.isDirectory()) { 682 target.mkdir(); 683 continue; 684 } 685 try (InputStream is = zf.getInputStream(ze); 686 FileOutputStream fos = new FileOutputStream(target)) 687 { 688 while (is.available() > 0) { 689 fos.write(is.read()); 690 } 691 } 692 } 693 } 694 } 695 }