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