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 import static sun.nio.fs.UnixNativeDispatcher.*;
34 
35 /**
36  * Unix implementation of java.nio.file.DirectoryStream
37  */
38 
39 class UnixDirectoryStream
40     implements DirectoryStream<Path>
41 {
42     // path to directory when originally opened
43     private final UnixPath dir;
44 
45     // directory pointer (returned by opendir)
46     private final long dp;
47 
48     // filter (may be null)
49     private final DirectoryStream.Filter<? super Path> filter;
50 
51     // used to coorindate closing of directory stream
52     private final ReentrantReadWriteLock streamLock =
53         new ReentrantReadWriteLock(true);
54 
55     // indicates if directory stream is open (synchronize on closeLock)
56     private volatile boolean isClosed;
57 
58     // directory iterator
59     private Iterator<Path> iterator;
60 
61     /**
62      * Initializes a new instance
63      */
UnixDirectoryStream(UnixPath dir, long dp, DirectoryStream.Filter<? super Path> filter)64     UnixDirectoryStream(UnixPath dir, long dp, DirectoryStream.Filter<? super Path> filter) {
65         this.dir = dir;
66         this.dp = dp;
67         this.filter = filter;
68     }
69 
directory()70     protected final UnixPath directory() {
71         return dir;
72     }
73 
readLock()74     protected final Lock readLock() {
75         return streamLock.readLock();
76     }
77 
writeLock()78     protected final Lock writeLock() {
79         return streamLock.writeLock();
80     }
81 
isOpen()82     protected final boolean isOpen() {
83         return !isClosed;
84     }
85 
closeImpl()86     protected final boolean closeImpl() throws IOException {
87         if (!isClosed) {
88             isClosed = true;
89             try {
90                 closedir(dp);
91             } catch (UnixException x) {
92                 throw new IOException(x.errorString());
93             }
94             return true;
95         } else {
96             return false;
97         }
98     }
99 
100     @Override
close()101     public void close()
102         throws IOException
103     {
104         writeLock().lock();
105         try {
106             closeImpl();
107         } finally {
108             writeLock().unlock();
109         }
110     }
111 
iterator(DirectoryStream<Path> ds)112     protected final Iterator<Path> iterator(DirectoryStream<Path> ds) {
113         if (isClosed) {
114             throw new IllegalStateException("Directory stream is closed");
115         }
116         synchronized (this) {
117             if (iterator != null)
118                 throw new IllegalStateException("Iterator already obtained");
119             iterator = new UnixDirectoryIterator(ds);
120             return iterator;
121         }
122     }
123 
124     @Override
iterator()125     public Iterator<Path> iterator() {
126         return iterator(this);
127     }
128 
129     /**
130      * Iterator implementation
131      */
132     private class UnixDirectoryIterator implements Iterator<Path> {
133         private final DirectoryStream<Path> stream;
134 
135         // true when at EOF
136         private boolean atEof;
137 
138         // next entry to return
139         private Path nextEntry;
140 
UnixDirectoryIterator(DirectoryStream<Path> stream)141         UnixDirectoryIterator(DirectoryStream<Path> stream) {
142             atEof = false;
143             this.stream = stream;
144         }
145 
146         // Return true if file name is "." or ".."
isSelfOrParent(byte[] nameAsBytes)147         private boolean isSelfOrParent(byte[] nameAsBytes) {
148             if (nameAsBytes[0] == '.') {
149                 if ((nameAsBytes.length == 1) ||
150                     (nameAsBytes.length == 2 && nameAsBytes[1] == '.')) {
151                     return true;
152                 }
153             }
154             return false;
155         }
156 
157         // Returns next entry (or null)
readNextEntry()158         private Path readNextEntry() {
159             assert Thread.holdsLock(this);
160 
161             for (;;) {
162                 byte[] nameAsBytes = null;
163 
164                 // prevent close while reading
165                 readLock().lock();
166                 try {
167                     if (isOpen()) {
168                         nameAsBytes = readdir(dp);
169                     }
170                 } catch (UnixException x) {
171                     IOException ioe = x.asIOException(dir);
172                     throw new DirectoryIteratorException(ioe);
173                 } finally {
174                     readLock().unlock();
175                 }
176 
177                 // EOF
178                 if (nameAsBytes == null) {
179                     atEof = true;
180                     return null;
181                 }
182 
183                 // ignore "." and ".."
184                 if (!isSelfOrParent(nameAsBytes)) {
185                     Path entry = dir.resolve(nameAsBytes);
186 
187                     // return entry if no filter or filter accepts it
188                     try {
189                         if (filter == null || filter.accept(entry))
190                             return entry;
191                     } catch (IOException ioe) {
192                         throw new DirectoryIteratorException(ioe);
193                     }
194                 }
195             }
196         }
197 
198         @Override
hasNext()199         public synchronized boolean hasNext() {
200             if (nextEntry == null && !atEof)
201                 nextEntry = readNextEntry();
202             return nextEntry != null;
203         }
204 
205         @Override
next()206         public synchronized Path next() {
207             Path result;
208             if (nextEntry == null && !atEof) {
209                 result = readNextEntry();
210             } else {
211                 result = nextEntry;
212                 nextEntry = null;
213             }
214             if (result == null)
215                 throw new NoSuchElementException();
216             return result;
217         }
218 
219         @Override
remove()220         public void remove() {
221             throw new UnsupportedOperationException();
222         }
223     }
224 }
225