1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import java.io.ByteArrayInputStream;
  24 import java.io.ByteArrayOutputStream;
  25 import java.io.File;
  26 import java.io.FilePermission;
  27 import java.io.IOException;
  28 import java.nio.channels.FileChannel;
  29 import java.nio.file.Files;
  30 import java.nio.file.Paths;
  31 import static java.nio.file.StandardOpenOption.CREATE_NEW;
  32 import static java.nio.file.StandardOpenOption.WRITE;
  33 import java.security.CodeSource;
  34 import java.security.Permission;
  35 import java.security.PermissionCollection;
  36 import java.security.Permissions;
  37 import java.security.Policy;
  38 import java.security.ProtectionDomain;
  39 import java.util.Arrays;
  40 import java.util.Collections;
  41 import java.util.Enumeration;
  42 import java.util.List;
  43 import java.util.Properties;
  44 import java.util.PropertyPermission;
  45 import java.util.UUID;
  46 import java.util.concurrent.atomic.AtomicBoolean;
  47 import java.util.logging.FileHandler;
  48 import java.util.logging.LogManager;
  49 import java.util.logging.LoggingPermission;
  50 
  51 /**
  52  * @test
  53  * @bug 8059269
  54  * @summary tests that using a simple (non composite) pattern does not lead
  55  *        to NPE when the lock file already exists.
  56  * @run main/othervm FileHandlerPath UNSECURE
  57  * @run main/othervm FileHandlerPath SECURE
  58  * @author danielfuchs
  59  * @key randomness
  60  */
  61 public class FileHandlerPath {
  62 
  63     /**
  64      * We will test the simple pattern in two configurations.
  65      * UNSECURE: No security manager.
  66      * SECURE: With the security manager present - and the required
  67      *         permissions granted.
  68      */
  69     public static enum TestCase {
  70         UNSECURE, SECURE;
  71         public void run(Properties propertyFile) throws Exception {
  72             System.out.println("Running test case: " + name());
  73             Configure.setUp(this, propertyFile);
  74             test(this.name() + " " + propertyFile.getProperty("test.name"), propertyFile);
  75         }
  76     }
  77 
  78 
  79     // Use a random name provided by UUID to avoid collision with other tests
  80     final static String logFile = FileHandlerPath.class.getSimpleName() + "_"
  81                 + UUID.randomUUID().toString() + ".log";
  82     final static String tmpLogFile;
  83     final static String userDir = System.getProperty("user.dir");
  84     final static String tmpDir = System.getProperty("java.io.tmpdir");
  85     private static final List<Properties> properties;
  86     static {
  87         tmpLogFile = new File(tmpDir, logFile).toString();
  88         Properties props1 = new Properties();
  89         Properties props2 = new Properties();
  90         props1.setProperty("test.name", "relative file");
  91         props1.setProperty("test.file.name", logFile);
  92         props1.setProperty(FileHandler.class.getName() + ".pattern", logFile);
  93         props1.setProperty(FileHandler.class.getName() + ".count", "1");
  94         props2.setProperty("test.name", "absoluste file");
  95         props2.setProperty("test.file.name", tmpLogFile);
  96         props2.setProperty(FileHandler.class.getName() + ".pattern", "%t/" + logFile);
  97         props2.setProperty(FileHandler.class.getName() + ".count", "1");
  98         properties = Collections.unmodifiableList(Arrays.asList(
  99                     props1,
 100                     props2));
 101     }
 102 
 103     public static void main(String... args) throws Exception {
 104 
 105         if (args == null || args.length == 0) {
 106             args = new String[] {
 107                 TestCase.UNSECURE.name(),
 108                 TestCase.SECURE.name(),
 109             };
 110         }
 111 
 112         // Sanity checks
 113 
 114         if (!Files.isWritable(Paths.get(userDir))) {
 115             throw new RuntimeException(userDir +
 116                     ": user.dir is not writable - can't run test.");
 117         }
 118         if (!Files.isWritable(Paths.get(tmpDir))) {
 119             throw new RuntimeException(tmpDir +
 120                     ": java.io.tmpdir is not writable - can't run test.");
 121         }
 122 
 123         File[] files = {
 124             new File(logFile),
 125             new File(tmpLogFile),
 126             new File(logFile+".1"),
 127             new File(tmpLogFile+".1"),
 128             new File(logFile+".lck"),
 129             new File(tmpLogFile+".lck"),
 130             new File(logFile+".1.lck"),
 131             new File(tmpLogFile+".1.lck")
 132         };
 133 
 134         for (File log : files) {
 135             if (log.exists()) {
 136                 throw new Exception(log +": file already exists - can't run test.");
 137             }
 138         }
 139 
 140         // Now start the real test
 141 
 142         try {
 143             for (String testName : args) {
 144                 for (Properties propertyFile : properties) {
 145                     TestCase test = TestCase.valueOf(testName);
 146                     test.run(propertyFile);
 147                 }
 148             }
 149         } finally {
 150             // Cleanup...
 151             Configure.doPrivileged(() -> {
 152                 for(File log : files) {
 153                     try {
 154                         final boolean isLockFile = log.getName().endsWith(".lck");
 155                         // lock file should already be deleted, except if the
 156                         // test failed in exception.
 157                         // log file should all be present, except if the test
 158                         // failed in exception.
 159                         if (log.exists()) {
 160                             if (!isLockFile) {
 161                                 System.out.println("deleting "+log.toString());
 162                             } else {
 163                                 System.err.println("deleting lock file "+log.toString());
 164                             }
 165                             log.delete();
 166                         } else {
 167                             if (!isLockFile) {
 168                                 System.err.println(log.toString() + ": not found.");
 169                             }
 170                         }
 171                     } catch (Throwable t) {
 172                         // should not happen
 173                         t.printStackTrace();
 174                     }
 175                 }
 176             });
 177         }
 178     }
 179 
 180     static class Configure {
 181         static Policy policy = null;
 182         static final AtomicBoolean allowAll = new AtomicBoolean(false);
 183         static void setUp(TestCase test, Properties propertyFile) {
 184             switch (test) {
 185                 case SECURE:
 186                     if (policy == null && System.getSecurityManager() != null) {
 187                         throw new IllegalStateException("SecurityManager already set");
 188                     } else if (policy == null) {
 189                         policy = new SimplePolicy(TestCase.SECURE, allowAll);
 190                         Policy.setPolicy(policy);
 191                         System.setSecurityManager(new SecurityManager());
 192                     }
 193                     if (System.getSecurityManager() == null) {
 194                         throw new IllegalStateException("No SecurityManager.");
 195                     }
 196                     if (policy == null) {
 197                         throw new IllegalStateException("policy not configured");
 198                     }
 199                     break;
 200                 case UNSECURE:
 201                     if (System.getSecurityManager() != null) {
 202                         throw new IllegalStateException("SecurityManager already set");
 203                     }
 204                     break;
 205                 default:
 206                     new InternalError("No such testcase: " + test);
 207             }
 208             doPrivileged(() -> {
 209                 try {
 210                     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
 211                     propertyFile.store(bytes, propertyFile.getProperty("test.name"));
 212                     ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
 213                     LogManager.getLogManager().readConfiguration(bais);
 214                 } catch (IOException ex) {
 215                     throw new RuntimeException(ex);
 216                 }
 217             });
 218         }
 219         static void doPrivileged(Runnable run) {
 220             allowAll.set(true);
 221             try {
 222                 run.run();
 223             } finally {
 224                 allowAll.set(false);
 225             }
 226         }
 227     }
 228 
 229     public static void test(String name, Properties props) throws Exception {
 230         System.out.println("Testing: " + name);
 231         String file = props.getProperty("test.file.name");
 232         // create the lock files first - in order to take the path that
 233         // used to trigger the NPE
 234         Files.createFile(Paths.get(file + ".lck"));
 235         Files.createFile(Paths.get(file + ".1.lck"));
 236         final FileHandler f1 = new FileHandler();
 237         final FileHandler f2 = new FileHandler();
 238         f1.close();
 239         f2.close();
 240         System.out.println("Success for " + name);
 241     }
 242 
 243 
 244     final static class PermissionsBuilder {
 245         final Permissions perms;
 246         public PermissionsBuilder() {
 247             this(new Permissions());
 248         }
 249         public PermissionsBuilder(Permissions perms) {
 250             this.perms = perms;
 251         }
 252         public PermissionsBuilder add(Permission p) {
 253             perms.add(p);
 254             return this;
 255         }
 256         public PermissionsBuilder addAll(PermissionCollection col) {
 257             if (col != null) {
 258                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 259                     perms.add(e.nextElement());
 260                 }
 261             }
 262             return this;
 263         }
 264         public Permissions toPermissions() {
 265             final PermissionsBuilder builder = new PermissionsBuilder();
 266             builder.addAll(perms);
 267             return builder.perms;
 268         }
 269     }
 270 
 271     public static class SimplePolicy extends Policy {
 272 
 273         final Permissions permissions;
 274         final Permissions allPermissions;
 275         final AtomicBoolean allowAll;
 276         public SimplePolicy(TestCase test, AtomicBoolean allowAll) {
 277             this.allowAll = allowAll;
 278             permissions = new Permissions();
 279             permissions.add(new LoggingPermission("control", null)); // needed by new FileHandler()
 280             permissions.add(new FilePermission("<<ALL FILES>>", "read")); // needed by new FileHandler()
 281             permissions.add(new FilePermission(logFile, "write,delete")); // needed by new FileHandler()
 282             permissions.add(new FilePermission(logFile+".lck", "write,delete")); // needed by FileHandler.close()
 283             permissions.add(new FilePermission(logFile+".1", "write,delete")); // needed by new FileHandler()
 284             permissions.add(new FilePermission(logFile+".1.lck", "write,delete")); // needed by FileHandler.close()
 285             permissions.add(new FilePermission(tmpLogFile, "write,delete")); // needed by new FileHandler()
 286             permissions.add(new FilePermission(tmpLogFile+".lck", "write,delete")); // needed by FileHandler.close()
 287             permissions.add(new FilePermission(tmpLogFile+".1", "write,delete")); // needed by new FileHandler()
 288             permissions.add(new FilePermission(tmpLogFile+".1.lck", "write,delete")); // needed by FileHandler.close()
 289             permissions.add(new FilePermission(userDir, "write")); // needed by new FileHandler()
 290             permissions.add(new FilePermission(tmpDir, "write")); // needed by new FileHandler()
 291             permissions.add(new PropertyPermission("user.dir", "read"));
 292             permissions.add(new PropertyPermission("java.io.tmpdir", "read"));
 293             allPermissions = new Permissions();
 294             allPermissions.add(new java.security.AllPermission());
 295         }
 296 
 297         @Override
 298         public boolean implies(ProtectionDomain domain, Permission permission) {
 299             if (allowAll.get()) return allPermissions.implies(permission);
 300             return permissions.implies(permission);
 301         }
 302 
 303         @Override
 304         public PermissionCollection getPermissions(CodeSource codesource) {
 305             return new PermissionsBuilder().addAll(allowAll.get()
 306                     ? allPermissions : permissions).toPermissions();
 307         }
 308 
 309         @Override
 310         public PermissionCollection getPermissions(ProtectionDomain domain) {
 311             return new PermissionsBuilder().addAll(allowAll.get()
 312                     ? allPermissions : permissions).toPermissions();
 313         }
 314     }
 315 
 316 }