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