1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package libcore.java.nio.file;
18
19import org.junit.Before;
20import org.junit.Rule;
21import org.junit.Test;
22import org.junit.rules.TestRule;
23
24import java.io.IOException;
25import java.nio.ByteBuffer;
26import java.nio.channels.NonWritableChannelException;
27import java.nio.channels.SeekableByteChannel;
28import java.nio.file.ClosedDirectoryStreamException;
29import java.nio.file.DirectoryNotEmptyException;
30import java.nio.file.DirectoryStream;
31import java.nio.file.FileSystemException;
32import java.nio.file.Files;
33import java.nio.file.LinkOption;
34import java.nio.file.NoSuchFileException;
35import java.nio.file.NotDirectoryException;
36import java.nio.file.OpenOption;
37import java.nio.file.Path;
38import java.nio.file.Paths;
39import java.nio.file.SecureDirectoryStream;
40import java.nio.file.attribute.BasicFileAttributeView;
41import java.util.Arrays;
42import java.util.HashSet;
43import java.util.Iterator;
44import java.util.Set;
45
46import libcore.junit.util.ResourceLeakageDetector;
47import libcore.junit.util.ResourceLeakageDetector.LeakageDetectorRule;
48
49import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA;
50import static libcore.java.nio.file.FilesSetup.writeToFile;
51import static org.junit.Assert.assertEquals;
52import static org.junit.Assert.assertFalse;
53import static org.junit.Assert.assertTrue;
54import static org.junit.Assert.fail;
55
56public class DefaultSecureDirectoryStreamTest {
57
58    Path path_root;
59    Path path_dir1;
60    Path path_dir2;
61    Path path_dir3;
62    Path path_f1;
63    Path path_f2;
64    Path path_f3;
65    Path path_dir4;
66
67    @Rule
68    public FilesSetup filesSetup = new FilesSetup();
69    @Rule
70    public LeakageDetectorRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule();
71
72    @Before
73    public void setup() throws Exception {
74
75        // Initial setup of directory.
76        path_root = filesSetup.getPathInTestDir("dir");
77        path_dir1 = filesSetup.getPathInTestDir("dir/dir1");
78        path_dir2 = filesSetup.getPathInTestDir("dir/dir2");
79        path_dir3 = filesSetup.getPathInTestDir("dir/dir3");
80        path_dir4 = filesSetup.getPathInTestDir("dir/dir1/dir4");
81
82        path_f1 = filesSetup.getPathInTestDir("dir/f1");
83        path_f2 = filesSetup.getPathInTestDir("dir/f2");
84        path_f3 = filesSetup.getPathInTestDir("dir/f3");
85
86        Files.createDirectory(path_root);
87        Files.createDirectory(path_dir1);
88        Files.createDirectory(path_dir2);
89        Files.createDirectory(path_dir3);
90        Files.createDirectory(path_dir4);
91        Files.createFile(path_f1);
92        Files.createFile(path_f2);
93        Files.createFile(path_f3);
94    }
95
96    @Test
97    public void testIterator() throws IOException {
98        HashSet<Path> pathsSet = new HashSet<>();
99        HashSet<Path> expectedPathsSet = new HashSet<>();
100
101        expectedPathsSet.add(path_dir1);
102        expectedPathsSet.add(path_dir2);
103        expectedPathsSet.add(path_dir3);
104
105        // Filter all the directories.
106        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_root,
107                file -> Files.isDirectory(file))) {
108            Iterator<Path> directoryStreamIterator = directoryStream.iterator();
109            directoryStreamIterator.forEachRemaining(path -> pathsSet.add(path));
110            assertEquals(expectedPathsSet, pathsSet);
111        }
112    }
113
114    @Test
115    public void testIterator_calledTwice() throws IOException {
116        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_root,
117                file -> Files.isDirectory(file))) {
118            directoryStream.iterator();
119            try {
120                directoryStream.iterator();
121                fail();
122            } catch (IllegalStateException expected) {}
123        }
124    }
125
126    @Test
127    public void testIterator_afterClose() throws IOException {
128        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_root,
129                file -> Files.isDirectory(file));
130        directoryStream.close();
131        try {
132            directoryStream.iterator();
133            fail();
134        } catch (IllegalStateException expected) {}
135    }
136
137    @Test
138    public void test_newDirectoryStream() throws IOException {
139        HashSet<Path> pathsSet = new HashSet<>();
140        HashSet<Path> expectedPathsSet = new HashSet<>();
141
142        expectedPathsSet.add(path_dir4);
143
144        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
145                Files.newDirectoryStream(path_root);
146             DirectoryStream<Path> ds_path_dir1 =  ds_path_root.newDirectoryStream(path_root.
147                     relativize(path_dir1))) {
148
149            ds_path_dir1.forEach(path -> pathsSet.add(path));
150            assertEquals(expectedPathsSet, pathsSet);
151        }
152    }
153
154    @Test
155    public void test_newDirectoryStream_symbolicLink() throws IOException {
156        Path symlinkPath = Paths.get(path_dir1.toString(), "symlink");
157        Files.createSymbolicLink(symlinkPath, path_dir3);
158        assertTrue(Files.isSymbolicLink(symlinkPath));
159
160        try (SecureDirectoryStream<Path> ds_path_dir1 = (SecureDirectoryStream<Path>)
161                Files.newDirectoryStream(path_root)) {
162            try (DirectoryStream<Path> ds_path_dir2 =  ds_path_dir1.newDirectoryStream(path_root.
163                    relativize(symlinkPath), LinkOption.NOFOLLOW_LINKS)) {
164                fail();
165            } catch (FileSystemException expected) {}
166        }
167    }
168
169    @Test
170    public void test_newDirectoryStream_Exception() throws IOException {
171
172        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
173                Files.newDirectoryStream(path_root)) {
174            // When file is not a directory.
175            try (DirectoryStream<Path> ds_path_dir1 =  ds_path_root.newDirectoryStream(path_root.
176                    relativize(path_f1))) {
177                fail();
178            } catch (NotDirectoryException expected) {}
179
180
181            // NPE
182            try (DirectoryStream<Path> ds_path_dir1 =  ds_path_root.newDirectoryStream(null)) {
183                fail();
184            } catch (NullPointerException expected) {}
185
186            // NPE
187            try (DirectoryStream<Path> ds_path_dir1 =  ds_path_root.newDirectoryStream(path_root.
188                    relativize(path_f1), null)) {
189                fail();
190            } catch (NullPointerException expected) {}
191
192            // When stream is closed.
193            ds_path_root.close();
194            try (DirectoryStream<Path> ds_path_dir1 =  ds_path_root.newDirectoryStream(path_root.
195                    relativize(path_dir1))) {
196                fail();
197            } catch (ClosedDirectoryStreamException expected) {}
198        }
199    }
200
201    @Test
202    public void test_newByteChannel() throws IOException {
203        Set<OpenOption> set = new HashSet<OpenOption>();
204
205        // When file doesn't exist.
206        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
207                Files.newDirectoryStream(path_root)) {
208
209            try (SeekableByteChannel sbc = ds_path_root.newByteChannel(filesSetup.getTestPath(),
210                    set)) {
211                fail();
212            } catch (NoSuchFileException expected) {
213                assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
214            }
215
216            // When file exists.
217            // File opens in READ mode by default. The channel is non writable by default.
218            try (SeekableByteChannel sbc = ds_path_root.newByteChannel(
219                    path_root.relativize(path_f1), set)) {
220                sbc.write(ByteBuffer.allocate(10));
221                fail();
222            } catch (NonWritableChannelException expected) {
223            }
224
225            // Read a file.
226            writeToFile(path_f1, TEST_FILE_DATA);
227            try (SeekableByteChannel sbc = ds_path_root.newByteChannel(
228                    path_root.relativize(path_f1), set)) {
229                ByteBuffer readBuffer = ByteBuffer.allocate(10);
230                int bytesReadCount = sbc.read(readBuffer);
231
232                String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount),
233                        "UTF-8");
234                assertEquals(TEST_FILE_DATA, readData);
235            }
236
237            // when directory stream is closed.
238            ds_path_root.close();
239            try (SeekableByteChannel sbc = ds_path_root.newByteChannel(
240                    path_root.relativize(path_f1), set)) {
241                fail();
242            } catch (ClosedDirectoryStreamException expected) {}
243        }
244    }
245
246    @Test
247    public void test_newByteChannel_NPE() throws IOException {
248        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
249                Files.newDirectoryStream(path_root)) {
250            Set<OpenOption> set = new HashSet<OpenOption>();
251            try (SeekableByteChannel sbc = ds_path_root.newByteChannel(null, set)) {
252                fail();
253            } catch (NullPointerException expected) {
254            }
255
256            try (SeekableByteChannel sbc = ds_path_root.newByteChannel(filesSetup.getDataFilePath(),
257                    null)) {
258                fail();
259            } catch (NullPointerException expected) {
260            }
261        }
262    }
263
264    @Test
265    public void test_deleteFile() throws IOException {
266        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
267                Files.newDirectoryStream(path_root)) {
268            ds_path_root.deleteFile(path_root.relativize(path_f1));
269            assertFalse(Files.exists(path_f1));
270
271            // --- Exceptions ---
272            // When the file is a directory.
273            try {
274                ds_path_root.deleteFile(path_root.relativize(path_dir1));
275                fail();
276            } catch (FileSystemException expected) {}
277
278            // When file doesn't exists.
279            try {
280                ds_path_root.deleteFile(filesSetup.getTestPath());
281                fail();
282            } catch (NoSuchFileException expected) {}
283
284            // NullPointerException
285            try {
286                ds_path_root.deleteFile(null);
287                fail();
288            } catch (NullPointerException expected) {}
289
290            // When the directory stream is closed.
291            ds_path_root.close();
292            try {
293                ds_path_root.deleteFile(path_root.relativize(path_f2));
294                fail();
295            } catch (ClosedDirectoryStreamException expected) {}
296
297        }
298    }
299
300    @Test
301    public void test_deleteDirectory() throws IOException {
302        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
303                Files.newDirectoryStream(path_root)) {
304            ds_path_root.deleteDirectory(path_root.relativize(path_dir2));
305            assertFalse(Files.exists(path_dir2));
306
307            // When file is not a directory.
308            try {
309                ds_path_root.deleteDirectory(path_root.relativize(path_f1));
310                fail();
311            } catch (FileSystemException expected) {}
312
313            // When path doesn't exists.
314            try {
315                ds_path_root.deleteDirectory(filesSetup.getTestPath());
316                fail();
317            } catch (NoSuchFileException expected) {}
318
319            // When the directory is not empty.
320            try {
321                ds_path_root.deleteDirectory(path_root.relativize(path_dir1));
322                fail();
323            } catch (DirectoryNotEmptyException expected) {}
324
325            // --- Exceptions ---
326            // NullPointerException
327            try {
328                ds_path_root.deleteDirectory(null);
329                fail();
330            } catch (NullPointerException expected) {}
331
332            // When the directory stream is closed.
333            ds_path_root.close();
334            try {
335                ds_path_root.deleteDirectory(path_root.relativize(path_f2));
336                fail();
337            } catch (ClosedDirectoryStreamException expected) {}
338        }
339    }
340
341    @Test
342    public void test_move() throws IOException {
343        SecureDirectoryStream<Path> ds_path_dir1 = (SecureDirectoryStream<Path>)
344                Files.newDirectoryStream(path_dir1);
345
346        // moving a file.
347        ds_path_dir1.move(path_f1, ds_path_dir1, Paths.get("f1"));
348        assertTrue(Files.exists(Paths.get(path_dir1.toString(), "f1")));
349        assertFalse(Files.exists(path_f1));
350
351        // moving a directory.
352        ds_path_dir1.move(path_dir2, ds_path_dir1, Paths.get(path_dir4.toString(), "path_dir2"));
353        assertTrue(Files.exists(Paths.get(path_dir4.toString(), "path_dir2")));
354        assertFalse(Files.exists(path_dir2));
355
356        // when directory already exists of the same name.
357        ds_path_dir1.move(path_dir3, ds_path_dir1, Paths.get("path_dir2"));
358        assertTrue(Files.exists(Paths.get(path_dir1.toString(), "path_dir2")));
359        assertFalse(Files.exists(path_dir3));
360
361        // moving a non empty directory.
362        ds_path_dir1.move(path_dir1, ds_path_dir1, Paths.get(path_root.getParent().toString(),
363                "path_dir1"));
364        assertTrue(Files.exists(Paths.get(path_root.getParent().toString(), "path_dir1")));
365        assertFalse(Files.exists(path_dir1));
366
367        // --- Exceptions ---
368        // NullPointerException.
369        try {
370            ds_path_dir1.move(null, ds_path_dir1,
371                    Paths.get(path_root.getParent().toString(), "path_dir1"));
372            fail();
373        } catch (NullPointerException expected) {}
374
375        try {
376            ds_path_dir1.move(path_dir1, null,
377                    Paths.get(path_root.getParent().toString(), "path_dir1"));
378            fail();
379        } catch (NullPointerException expected) {}
380
381        try {
382            ds_path_dir1.move(path_dir1, ds_path_dir1,
383                    Paths.get(path_root.getParent().toString(), null));
384            fail();
385        } catch (NullPointerException expected) {}
386
387        try {
388            // when targetDir stream is closed.
389            ds_path_dir1.close();
390            ds_path_dir1.move(path_root, ds_path_dir1, path_f3);
391            fail();
392        } catch (ClosedDirectoryStreamException expected) {}
393    }
394
395    @Test
396    public void test_getFileAttributeView() throws IOException {
397        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
398                Files.newDirectoryStream(path_root)) {
399            BasicFileAttributeView fileAttributeView = ds_path_root
400                    .getFileAttributeView(BasicFileAttributeView.class);
401
402            assertFalse(fileAttributeView.readAttributes().isRegularFile());
403            assertTrue(fileAttributeView.readAttributes().isDirectory());
404            assertFalse(fileAttributeView.readAttributes().isSymbolicLink());
405
406            // --- Exceptions ---
407            // NullPointerException
408            try {
409                ds_path_root.getFileAttributeView(null);
410            } catch (NullPointerException expected) {}
411
412            // When directory stream is closed.
413            ds_path_root.close();
414            fileAttributeView = ds_path_root.getFileAttributeView(BasicFileAttributeView.class);
415            try {
416                fileAttributeView.readAttributes();
417                fail();
418            } catch (ClosedDirectoryStreamException expected) {}
419        }
420    }
421
422    @Test
423    public void test_getFileAttributeView_Path() throws IOException {
424        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
425                Files.newDirectoryStream(path_root)) {
426            BasicFileAttributeView fileAttributeView = ds_path_root.getFileAttributeView(
427                    path_root.relativize(path_dir1), BasicFileAttributeView.class);
428
429            assertFalse(fileAttributeView.readAttributes().isRegularFile());
430            assertTrue(fileAttributeView.readAttributes().isDirectory());
431            assertFalse(fileAttributeView.readAttributes().isSymbolicLink());
432
433            fileAttributeView = ds_path_root.getFileAttributeView(path_root.relativize(path_f1),
434                            BasicFileAttributeView.class);
435
436            assertTrue(fileAttributeView.readAttributes().isRegularFile());
437            assertFalse(fileAttributeView.readAttributes().isDirectory());
438            assertFalse(fileAttributeView.readAttributes().isSymbolicLink());
439
440            // When file is a symbolic link.
441            Path symlinkPath = Paths.get(path_root.toString(), "symlink");
442            Files.createSymbolicLink(symlinkPath, path_dir1);
443            assertTrue(Files.isSymbolicLink(symlinkPath));
444            // When file is a symbolic link and method is invoked with LinkOptions.NOFOLLOW_LINKS.
445            fileAttributeView = ds_path_root.getFileAttributeView(path_root.relativize(symlinkPath),
446                    BasicFileAttributeView.class);
447            assertTrue(fileAttributeView.readAttributes().isDirectory());
448
449            // --- Exceptions ---
450            try {
451                ds_path_root.getFileAttributeView(null, BasicFileAttributeView.class);
452                fail();
453            } catch (NullPointerException expected) {}
454
455            try {
456                ds_path_root.getFileAttributeView(path_root.relativize(path_f1), null);
457                fail();
458            } catch (NullPointerException expected) {}
459
460            // When directory stream is closed.
461            ds_path_root.close();
462            fileAttributeView = ds_path_root.getFileAttributeView(path_root.relativize(path_f1),
463                    BasicFileAttributeView.class);
464            try {
465                fileAttributeView.readAttributes();
466                fail();
467            } catch (ClosedDirectoryStreamException expected) {}
468        }
469    }
470
471    @Test
472    public void test_getFileAttributeView_Path_LinkOptions() throws IOException {
473        Path symlinkPath = Paths.get(path_root.toString(), "symlink");
474        Files.createSymbolicLink(symlinkPath, path_dir1);
475        assertTrue(Files.isSymbolicLink(symlinkPath));
476        // When file is a symbolic link and method is invoked with LinkOptions.NOFOLLOW_LINKS.
477        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
478                Files.newDirectoryStream(path_root)) {
479            BasicFileAttributeView fileAttributeView = ds_path_root.getFileAttributeView(
480                    path_root.relativize(symlinkPath), BasicFileAttributeView.class,
481                    LinkOption.NOFOLLOW_LINKS);
482            assertTrue(fileAttributeView.readAttributes().isSymbolicLink());
483        }
484
485        // When file is not a symbolic link.
486        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
487                Files.newDirectoryStream(path_root)) {
488            BasicFileAttributeView fileAttributeView = ds_path_root
489                    .getFileAttributeView(path_root.relativize(path_f1),
490                            BasicFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
491            assertTrue(fileAttributeView.readAttributes().isRegularFile());
492            assertFalse(fileAttributeView.readAttributes().isDirectory());
493            assertFalse(fileAttributeView.readAttributes().isSymbolicLink());
494        }
495
496        // --- Exceptions ---
497        // NullPointerException
498        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
499                Files.newDirectoryStream(path_root)) {
500            ds_path_root.getFileAttributeView(path_root.relativize(path_f1),
501                    BasicFileAttributeView.class, null);
502            fail();
503        } catch (NullPointerException expected) {}
504    }
505
506    @Test
507    public void testUnixSecureDirectoryStreamHasFinalizer() throws IOException {
508        try (SecureDirectoryStream<Path> ds_path_root = (SecureDirectoryStream<Path>)
509                Files.newDirectoryStream(path_root)) {
510            resourceLeakageDetectorRule.assertUnreleasedResourceCount(ds_path_root, 1);
511        }
512    }
513}
514