1 /*
2  * Copyright (c) 2008, 2011, 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.nio.file.attribute.BasicFileAttributes;
30 import java.util.Iterator;
31 import java.util.NoSuchElementException;
32 import java.io.IOException;
33 
34 import static sun.nio.fs.WindowsNativeDispatcher.*;
35 import static sun.nio.fs.WindowsConstants.*;
36 
37 /**
38  * Windows implementation of DirectoryStream
39  */
40 
41 class WindowsDirectoryStream
42     implements DirectoryStream<Path>
43 {
44     private final WindowsPath dir;
45     private final DirectoryStream.Filter<? super Path> filter;
46 
47     // handle to directory
48     private final long handle;
49     // first entry in the directory
50     private final String firstName;
51 
52     // buffer for WIN32_FIND_DATA structure that receives information about file
53     private final NativeBuffer findDataBuffer;
54 
55     private final Object closeLock = new Object();
56 
57     // need closeLock to access these
58     private boolean isOpen = true;
59     private Iterator<Path> iterator;
60 
61 
WindowsDirectoryStream(WindowsPath dir, DirectoryStream.Filter<? super Path> filter)62     WindowsDirectoryStream(WindowsPath dir, DirectoryStream.Filter<? super Path> filter)
63         throws IOException
64     {
65         this.dir = dir;
66         this.filter = filter;
67 
68         try {
69             // Need to append * or \* to match entries in directory.
70             String search = dir.getPathForWin32Calls();
71             char last = search.charAt(search.length() -1);
72             if (last == ':' || last == '\\') {
73                 search += "*";
74             } else {
75                 search += "\\*";
76             }
77 
78             FirstFile first = FindFirstFile(search);
79             this.handle = first.handle();
80             this.firstName = first.name();
81             this.findDataBuffer = WindowsFileAttributes.getBufferForFindData();
82         } catch (WindowsException x) {
83             if (x.lastError() == ERROR_DIRECTORY) {
84                 throw new NotDirectoryException(dir.getPathForExceptionMessage());
85             }
86             x.rethrowAsIOException(dir);
87 
88             // keep compiler happy
89             throw new AssertionError();
90         }
91     }
92 
93     @Override
close()94     public void close()
95         throws IOException
96     {
97         synchronized (closeLock) {
98             if (!isOpen)
99                 return;
100             isOpen = false;
101         }
102         findDataBuffer.release();
103         try {
104             FindClose(handle);
105         } catch (WindowsException x) {
106             x.rethrowAsIOException(dir);
107         }
108     }
109 
110     @Override
iterator()111     public Iterator<Path> iterator() {
112         if (!isOpen) {
113             throw new IllegalStateException("Directory stream is closed");
114         }
115         synchronized (this) {
116             if (iterator != null)
117                 throw new IllegalStateException("Iterator already obtained");
118             iterator = new WindowsDirectoryIterator(firstName);
119             return iterator;
120         }
121     }
122 
123     private class WindowsDirectoryIterator implements Iterator<Path> {
124         private boolean atEof;
125         private String first;
126         private Path nextEntry;
127         private String prefix;
128 
WindowsDirectoryIterator(String first)129         WindowsDirectoryIterator(String first) {
130             atEof = false;
131             this.first = first;
132             if (dir.needsSlashWhenResolving()) {
133                 prefix = dir.toString() + "\\";
134             } else {
135                 prefix = dir.toString();
136             }
137         }
138 
139         // links to self and parent directories are ignored
isSelfOrParent(String name)140         private boolean isSelfOrParent(String name) {
141             return name.equals(".") || name.equals("..");
142         }
143 
144         // applies filter and also ignores "." and ".."
acceptEntry(String s, BasicFileAttributes attrs)145         private Path acceptEntry(String s, BasicFileAttributes attrs) {
146             Path entry = WindowsPath
147                 .createFromNormalizedPath(dir.getFileSystem(), prefix + s, attrs);
148             try {
149                 if (filter.accept(entry))
150                     return entry;
151             } catch (IOException ioe) {
152                 throw new DirectoryIteratorException(ioe);
153             }
154             return null;
155         }
156 
157         // reads next directory entry
readNextEntry()158         private Path readNextEntry() {
159             // handle first element returned by search
160             if (first != null) {
161                 nextEntry = isSelfOrParent(first) ? null : acceptEntry(first, null);
162                 first = null;
163                 if (nextEntry != null)
164                     return nextEntry;
165             }
166 
167             for (;;) {
168                 String name = null;
169                 WindowsFileAttributes attrs;
170 
171                 // synchronize on closeLock to prevent close while reading
172                 synchronized (closeLock) {
173                     try {
174                         if (isOpen) {
175                             name = FindNextFile(handle, findDataBuffer.address());
176                         }
177                     } catch (WindowsException x) {
178                         IOException ioe = x.asIOException(dir);
179                         throw new DirectoryIteratorException(ioe);
180                     }
181 
182                     // NO_MORE_FILES or stream closed
183                     if (name == null) {
184                         atEof = true;
185                         return null;
186                     }
187 
188                     // ignore link to self and parent directories
189                     if (isSelfOrParent(name))
190                         continue;
191 
192                     // grab the attributes from the WIN32_FIND_DATA structure
193                     // (needs to be done while holding closeLock because close
194                     // will release the buffer)
195                     attrs = WindowsFileAttributes
196                         .fromFindData(findDataBuffer.address());
197                 }
198 
199                 // return entry if accepted by filter
200                 Path entry = acceptEntry(name, attrs);
201                 if (entry != null)
202                     return entry;
203             }
204         }
205 
206         @Override
hasNext()207         public synchronized boolean hasNext() {
208             if (nextEntry == null && !atEof)
209                 nextEntry = readNextEntry();
210             return nextEntry != null;
211         }
212 
213         @Override
next()214         public synchronized Path next() {
215             Path result = null;
216             if (nextEntry == null && !atEof) {
217                 result = readNextEntry();
218             } else {
219                 result = nextEntry;
220                 nextEntry = null;
221             }
222             if (result == null)
223                 throw new NoSuchElementException();
224             return result;
225         }
226 
227         @Override
remove()228         public void remove() {
229             throw new UnsupportedOperationException();
230         }
231     }
232 }
233