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-added: 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-added: 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-added: 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 // Android-added: CloseGuard support. finalize()238 protected void finalize() throws IOException { 239 if (guard != null) { 240 guard.warnIfOpen(); 241 } 242 243 close(); 244 } 245 } 246