1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  */
  23 
  24 /* @test
  25  * @bug 6595866
  26  * @summary Test java.io.File operations with sym links
  27  */
  28 
  29 import java.io.*;
  30 import java.nio.file.Path;
  31 import java.nio.file.attribute.*;
  32 import static java.nio.file.LinkOption.*;
  33 
  34 public class SymLinks {
  35     final static PrintStream out = System.out;
  36 
  37     final static File top = new File(System.getProperty("test.dir", "."));
  38 
  39     // files used by the test
  40 
  41     final static File file              = new File(top, "foofile");
  42     final static File link2file         = new File(top, "link2file");
  43     final static File link2link2file    = new File(top, "link2link2file");
  44 
  45     final static File dir               = new File(top, "foodir");
  46     final static File link2dir          = new File(top, "link2dir");
  47     final static File link2link2dir     = new File(top, "link2link2dir");
  48 
  49     final static File link2nobody       = new File(top, "link2nobody");
  50     final static File link2link2nobody  = new File(top, "link2link2nobody");
  51 
  52     /**
  53      * Setup files, directories, and sym links used by test.
  54      */
  55     static void setup() throws IOException {
  56         // link2link2file -> link2file -> foofile
  57         FileOutputStream fos = new FileOutputStream(file);
  58         try {
  59             fos.write(new byte[16*1024]);
  60         } finally {
  61             fos.close();
  62         }
  63         mklink(link2file, file);
  64         mklink(link2link2file, link2file);
  65 
  66         // link2link2dir -> link2dir -> dir
  67         assertTrue(dir.mkdir());
  68         mklink(link2dir, dir);
  69         mklink(link2link2dir, link2dir);
  70 
  71         // link2link2nobody -> link2nobody -> <does-not-exist>
  72         mklink(link2nobody, new File(top, "DoesNotExist"));
  73         mklink(link2link2nobody, link2nobody);
  74     }
  75 
  76     /**
  77      * Remove files, directories, and sym links used by test.
  78      */
  79     static void cleanup() throws IOException {
  80         if (file != null)
  81             file.delete();
  82         if (link2file != null)
  83             link2file.toPath().deleteIfExists();
  84         if (link2link2file != null)
  85             link2link2file.toPath().deleteIfExists();
  86         if (dir != null)
  87             dir.delete();
  88         if (link2dir != null)
  89             link2dir.toPath().deleteIfExists();
  90         if (link2link2dir != null)
  91             link2link2dir.toPath().deleteIfExists();
  92         if (link2nobody != null)
  93             link2nobody.toPath().deleteIfExists();
  94         if (link2link2nobody != null)
  95             link2link2nobody.toPath().deleteIfExists();
  96     }
  97 
  98     /**
  99      * Creates a sym link source->target
 100      */
 101     static void mklink(File source, File target) throws IOException {
 102         source.toPath().createSymbolicLink(target.toPath());
 103     }
 104 
 105     /**
 106      * Returns true if the "link" exists and is a sym link.
 107      */
 108     static boolean isSymLink(File link) {
 109          try {
 110             BasicFileAttributes attrs =
 111                 Attributes.readBasicFileAttributes(link.toPath(), NOFOLLOW_LINKS);
 112             return attrs.isSymbolicLink();
 113          } catch (IOException x) {
 114              return false;
 115          }
 116     }
 117 
 118     /**
 119      * Returns the last modified time of a sym link.
 120      */
 121     static long lastModifiedOfSymLink(File link) throws IOException {
 122         BasicFileAttributes attrs =
 123             Attributes.readBasicFileAttributes(link.toPath(), NOFOLLOW_LINKS);
 124         assertTrue(attrs.isSymbolicLink());
 125         return attrs.lastModifiedTime().toMillis();
 126     }
 127 
 128     /**
 129      * Returns true if sym links are supported on the file system where
 130      * "dir" exists.
 131      */
 132     static boolean supportsSymLinks(File dir) {
 133         Path link = dir.toPath().resolve("link");
 134         Path target = dir.toPath().resolve("target");
 135         try {
 136             link.createSymbolicLink(target);
 137             link.delete();
 138             return true;
 139         } catch (UnsupportedOperationException x) {
 140             return false;
 141         } catch (IOException x) {
 142             return false;
 143         }
 144     }
 145 
 146     static void assertTrue(boolean v) {
 147         if (!v) throw new RuntimeException("Test failed");
 148     }
 149 
 150     static void assertFalse(boolean v) {
 151         assertTrue(!v);
 152     }
 153 
 154     static void header(String h) {
 155         out.println();
 156         out.println();
 157         out.println("-- " + h + " --");
 158     }
 159 
 160     /**
 161      * Tests go here.
 162      */
 163     static void go() throws IOException {
 164 
 165         // check setup
 166         assertTrue(file.isFile());
 167         assertTrue(isSymLink(link2file));
 168         assertTrue(isSymLink(link2link2file));
 169         assertTrue(dir.isDirectory());
 170         assertTrue(isSymLink(link2dir));
 171         assertTrue(isSymLink(link2link2dir));
 172         assertTrue(isSymLink(link2nobody));
 173         assertTrue(isSymLink(link2link2nobody));
 174 
 175         header("createNewFile");
 176 
 177         assertFalse(file.createNewFile());
 178         assertFalse(link2file.createNewFile());
 179         assertFalse(link2link2file.createNewFile());
 180         assertFalse(dir.createNewFile());
 181         assertFalse(link2dir.createNewFile());
 182         assertFalse(link2link2dir.createNewFile());
 183         assertFalse(link2nobody.createNewFile());
 184         assertFalse(link2link2nobody.createNewFile());
 185 
 186         header("mkdir");
 187 
 188         assertFalse(file.mkdir());
 189         assertFalse(link2file.mkdir());
 190         assertFalse(link2link2file.mkdir());
 191         assertFalse(dir.mkdir());
 192         assertFalse(link2dir.mkdir());
 193         assertFalse(link2link2dir.mkdir());
 194         assertFalse(link2nobody.mkdir());
 195         assertFalse(link2link2nobody.mkdir());
 196 
 197         header("delete");
 198 
 199         File link = new File(top, "mylink");
 200         try {
 201             mklink(link, file);
 202             assertTrue(link.delete());
 203             assertTrue(!isSymLink(link));
 204             assertTrue(file.exists());
 205 
 206             mklink(link, link2file);
 207             assertTrue(link.delete());
 208             assertTrue(!isSymLink(link));
 209             assertTrue(link2file.exists());
 210 
 211             mklink(link, dir);
 212             assertTrue(link.delete());
 213             assertTrue(!isSymLink(link));
 214             assertTrue(dir.exists());
 215 
 216             mklink(link, link2dir);
 217             assertTrue(link.delete());
 218             assertTrue(!isSymLink(link));
 219             assertTrue(link2dir.exists());
 220 
 221             mklink(link, link2nobody);
 222             assertTrue(link.delete());
 223             assertTrue(!isSymLink(link));
 224             assertTrue(isSymLink(link2nobody));
 225 
 226         } finally {
 227             link.toPath().deleteIfExists();
 228         }
 229 
 230         header("renameTo");
 231 
 232         File newlink = new File(top, "newlink");
 233         assertTrue(link2file.renameTo(newlink));
 234         try {
 235             assertTrue(file.exists());
 236             assertTrue(isSymLink(newlink));
 237             assertTrue(!isSymLink(link2file));
 238         } finally {
 239             newlink.renameTo(link2file);  // restore link
 240         }
 241 
 242         assertTrue(link2dir.renameTo(newlink));
 243         try {
 244             assertTrue(dir.exists());
 245             assertTrue(isSymLink(newlink));
 246             assertTrue(!isSymLink(link2dir));
 247         } finally {
 248             newlink.renameTo(link2dir);  // restore link
 249         }
 250 
 251         header("list");
 252 
 253         final String name = "entry";
 254         File entry = new File(dir, name);
 255         try {
 256             assertTrue(dir.list().length == 0);   // directory should be empty
 257             assertTrue(link2dir.list().length == 0);
 258             assertTrue(link2link2dir.list().length == 0);
 259 
 260             assertTrue(entry.createNewFile());
 261             assertTrue(dir.list().length == 1);
 262             assertTrue(dir.list()[0].equals(name));
 263 
 264             // access directory by following links
 265             assertTrue(link2dir.list().length == 1);
 266             assertTrue(link2dir.list()[0].equals(name));
 267             assertTrue(link2link2dir.list().length == 1);
 268             assertTrue(link2link2dir.list()[0].equals(name));
 269 
 270             // files that are not directories
 271             assertTrue(link2file.list() == null);
 272             assertTrue(link2nobody.list() == null);
 273 
 274         } finally {
 275             entry.delete();
 276         }
 277 
 278         header("isXXX");
 279 
 280         assertTrue(file.isFile());
 281         assertTrue(link2file.isFile());
 282         assertTrue(link2link2file.isFile());
 283 
 284         assertTrue(dir.isDirectory());
 285         assertTrue(link2dir.isDirectory());
 286         assertTrue(link2link2dir.isDirectory());
 287 
 288         // on Windows we test with the DOS hidden attribute set
 289         if (System.getProperty("os.name").startsWith("Windows")) {
 290             DosFileAttributeView view = file.toPath()
 291                 .getFileAttributeView(DosFileAttributeView.class);
 292             view.setHidden(true);
 293             try {
 294                 assertTrue(file.isHidden());
 295                 assertTrue(link2file.isHidden());
 296                 assertTrue(link2link2file.isHidden());
 297             } finally {
 298                 view.setHidden(false);
 299             }
 300             assertFalse(file.isHidden());
 301             assertFalse(link2file.isHidden());
 302             assertFalse(link2link2file.isHidden());
 303         }
 304 
 305         header("length");
 306 
 307         long len = file.length();
 308         assertTrue(len > 0L);
 309         // these tests should follow links
 310         assertTrue(link2file.length() == len);
 311         assertTrue(link2link2file.length() == len);
 312         assertTrue(link2nobody.length() == 0L);
 313 
 314         header("lastModified / setLastModified");
 315 
 316         // need time to diff between link and file
 317         long origLastModified = file.lastModified();
 318         assertTrue(origLastModified != 0L);
 319         try { Thread.sleep(2000); } catch (InterruptedException x) { }
 320         file.setLastModified(System.currentTimeMillis());
 321 
 322         long lastModified = file.lastModified();
 323         assertTrue(lastModified != origLastModified);
 324         assertTrue(lastModifiedOfSymLink(link2file) != lastModified);
 325         assertTrue(lastModifiedOfSymLink(link2link2file) != lastModified);
 326         assertTrue(link2file.lastModified() == lastModified);
 327         assertTrue(link2link2file.lastModified() == lastModified);
 328         assertTrue(link2nobody.lastModified() == 0L);
 329 
 330         origLastModified = dir.lastModified();
 331         assertTrue(origLastModified != 0L);
 332         dir.setLastModified(0L);
 333         assertTrue(dir.lastModified() == 0L);
 334         assertTrue(link2dir.lastModified() == 0L);
 335         assertTrue(link2link2dir.lastModified() == 0L);
 336         dir.setLastModified(origLastModified);
 337 
 338         header("setXXX / canXXX");
 339 
 340         assertTrue(file.canRead());
 341         assertTrue(file.canWrite());
 342         assertTrue(link2file.canRead());
 343         assertTrue(link2file.canWrite());
 344         assertTrue(link2link2file.canRead());
 345         assertTrue(link2link2file.canWrite());
 346 
 347         if (file.setReadOnly()) {
 348             assertFalse(file.canWrite());
 349             assertFalse(link2file.canWrite());
 350             assertFalse(link2link2file.canWrite());
 351 
 352             assertTrue(file.setWritable(true));             // make writable
 353             assertTrue(file.canWrite());
 354             assertTrue(link2file.canWrite());
 355             assertTrue(link2link2file.canWrite());
 356 
 357             assertTrue(link2file.setReadOnly());            // make read only
 358             assertFalse(file.canWrite());
 359             assertFalse(link2file.canWrite());
 360             assertFalse(link2link2file.canWrite());
 361 
 362             assertTrue(link2link2file.setWritable(true));   // make writable
 363             assertTrue(file.canWrite());
 364             assertTrue(link2file.canWrite());
 365             assertTrue(link2link2file.canWrite());
 366         }
 367     }
 368 
 369     public static void main(String[] args) throws IOException {
 370         if (supportsSymLinks(top)) {
 371             try {
 372                 setup();
 373                 go();
 374             } finally {
 375                 cleanup();
 376             }
 377         }
 378     }
 379 
 380 }