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