1 /*
2  * Copyright (c) 2008, 2010, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.nio.fs;
27 
28 import java.nio.file.*;
29 import java.util.Iterator;
30 import java.util.NoSuchElementException;
31 import java.util.concurrent.locks.*;
32 import java.io.IOException;
33 
34 import dalvik.system.CloseGuard;
35 
36 import static sun.nio.fs.UnixNativeDispatcher.*;
37 
38 /**
39  * Unix implementation of java.nio.file.DirectoryStream
40  */
41 
42 class UnixDirectoryStream
43     implements DirectoryStream<Path>
44 {
45     // path to directory when originally opened
46     private final UnixPath dir;
47 
48     // directory pointer (returned by opendir)
49     private final long dp;
50 
51     // filter (may be null)
52     private final DirectoryStream.Filter<? super Path> filter;
53 
54     // used to coorindate closing of directory stream
55     private final ReentrantReadWriteLock streamLock =
56         new ReentrantReadWriteLock(true);
57 
58     // indicates if directory stream is open (synchronize on closeLock)
59     private volatile boolean isClosed;
60 
61     // directory iterator
62     private Iterator<Path> iterator;
63 
64     // Android-changed: Add CloseGuard support.
65     private final CloseGuard guard = CloseGuard.get();
66 
67     /**
68      * Initializes a new instance
69      */
UnixDirectoryStream(UnixPath dir, long dp, DirectoryStream.Filter<? super Path> filter)70     UnixDirectoryStream(UnixPath dir, long dp, DirectoryStream.Filter<? super Path> filter) {
71         this.dir = dir;
72         this.dp = dp;
73         this.filter = filter;
74 
75         // Android-changed: Add CloseGuard support.
76         guard.open("close");
77     }
78 
directory()79     protected final UnixPath directory() {
80         return dir;
81     }
82 
readLock()83     protected final Lock readLock() {
84         return streamLock.readLock();
85     }
86 
writeLock()87     protected final Lock writeLock() {
88         return streamLock.writeLock();
89     }
90 
isOpen()91     protected final boolean isOpen() {
92         return !isClosed;
93     }
94 
closeImpl()95     protected final boolean closeImpl() throws IOException {
96         if (!isClosed) {
97             isClosed = true;
98             try {
99                 closedir(dp);
100             } catch (UnixException x) {
101                 throw new IOException(x.errorString());
102             }
103 
104             // Android-changed: Add CloseGuard support.
105             guard.close();
106             return true;
107         } else {
108             return false;
109         }
110     }
111 
112     @Override
close()113     public void close()
114         throws IOException
115     {
116         writeLock().lock();
117         try {
118             closeImpl();
119         } finally {
120             writeLock().unlock();
121         }
122     }
123 
iterator(DirectoryStream<Path> ds)124     protected final Iterator<Path> iterator(DirectoryStream<Path> ds) {
125         if (isClosed) {
126             throw new IllegalStateException("Directory stream is closed");
127         }
128         synchronized (this) {
129             if (iterator != null)
130                 throw new IllegalStateException("Iterator already obtained");
131             iterator = new UnixDirectoryIterator(ds);
132             return iterator;
133         }
134     }
135 
136     @Override
iterator()137     public Iterator<Path> iterator() {
138         return iterator(this);
139     }
140 
141     /**
142      * Iterator implementation
143      */
144     private class UnixDirectoryIterator implements Iterator<Path> {
145         private final DirectoryStream<Path> stream;
146 
147         // true when at EOF
148         private boolean atEof;
149 
150         // next entry to return
151         private Path nextEntry;
152 
UnixDirectoryIterator(DirectoryStream<Path> stream)153         UnixDirectoryIterator(DirectoryStream<Path> stream) {
154             atEof = false;
155             this.stream = stream;
156         }
157 
158         // Return true if file name is "." or ".."
isSelfOrParent(byte[] nameAsBytes)159         private boolean isSelfOrParent(byte[] nameAsBytes) {
160             if (nameAsBytes[0] == '.') {
161                 if ((nameAsBytes.length == 1) ||
162                     (nameAsBytes.length == 2 && nameAsBytes[1] == '.')) {
163                     return true;
164                 }
165             }
166             return false;
167         }
168 
169         // Returns next entry (or null)
readNextEntry()170         private Path readNextEntry() {
171             assert Thread.holdsLock(this);
172 
173             for (;;) {
174                 byte[] nameAsBytes = null;
175 
176                 // prevent close while reading
177                 readLock().lock();
178                 try {
179                     if (isOpen()) {
180                         nameAsBytes = readdir(dp);
181                     }
182                 } catch (UnixException x) {
183                     IOException ioe = x.asIOException(dir);
184                     throw new DirectoryIteratorException(ioe);
185                 } finally {
186                     readLock().unlock();
187                 }
188 
189                 // EOF
190                 if (nameAsBytes == null) {
191                     atEof = true;
192                     return null;
193                 }
194 
195                 // ignore "." and ".."
196                 if (!isSelfOrParent(nameAsBytes)) {
197                     Path entry = dir.resolve(nameAsBytes);
198 
199                     // return entry if no filter or filter accepts it
200                     try {
201                         if (filter == null || filter.accept(entry))
202                             return entry;
203                     } catch (IOException ioe) {
204                         throw new DirectoryIteratorException(ioe);
205                     }
206                 }
207             }
208         }
209 
210         @Override
hasNext()211         public synchronized boolean hasNext() {
212             if (nextEntry == null && !atEof)
213                 nextEntry = readNextEntry();
214             return nextEntry != null;
215         }
216 
217         @Override
next()218         public synchronized Path next() {
219             Path result;
220             if (nextEntry == null && !atEof) {
221                 result = readNextEntry();
222             } else {
223                 result = nextEntry;
224                 nextEntry = null;
225             }
226             if (result == null)
227                 throw new NoSuchElementException();
228             return result;
229         }
230 
231         @Override
remove()232         public void remove() {
233             throw new UnsupportedOperationException();
234         }
235     }
236 
237     /**
238      * Cleans up if the user forgets to close it.
239      */
240     // Android-changed: Add CloseGuard support.
finalize()241     protected void finalize() throws IOException {
242         if (guard != null) {
243             guard.warnIfOpen();
244         }
245 
246         close();
247     }
248 }
249