1 /*
   2  * Copyright (c) 2008, 2009, 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 
  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
  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
 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 
 128         WindowsDirectoryIterator(String first) {
 129             atEof = false;
 130             this.first = first;
 131         }
 132 
 133         // applies filter and also ignores "." and ".."
 134         private Path acceptEntry(String s, BasicFileAttributes attrs) {
 135             if (s.equals(".") || s.equals(".."))
 136                 return null;
 137             if (dir.needsSlashWhenResolving()) {
 138                 StringBuilder sb = new StringBuilder(dir.toString());
 139                 sb.append('\\');
 140                 sb.append(s);
 141                 s = sb.toString();
 142             } else {
 143                 s = dir + s;
 144             }
 145             Path entry = WindowsPath
 146                 .createFromNormalizedPath(dir.getFileSystem(), s, attrs);
 147             try {
 148                 if (filter.accept(entry))
 149                     return entry;
 150             } catch (IOException ioe) {
 151                 throw new DirectoryIteratorException(ioe);
 152             }
 153             return null;
 154         }
 155 
 156         // reads next directory entry
 157         private Path readNextEntry() {
 158             // handle first element returned by search
 159             if (first != null) {
 160                 nextEntry = acceptEntry(first, null);
 161                 first = null;
 162                 if (nextEntry != null)
 163                     return nextEntry;
 164             }
 165 
 166             for (;;) {
 167                 String name = null;
 168                 WindowsFileAttributes attrs;
 169 
 170                 // synchronize on closeLock to prevent close while reading
 171                 synchronized (closeLock) {
 172                     try {
 173                         if (isOpen) {
 174                             name = FindNextFile(handle, findDataBuffer.address());
 175                         }
 176                     } catch (WindowsException x) {
 177                         IOException ioe = x.asIOException(dir);
 178                         throw new DirectoryIteratorException(ioe);
 179                     }
 180 
 181                     // NO_MORE_FILES or stream closed
 182                     if (name == null) {
 183                         atEof = true;
 184                         return null;
 185                     }
 186 
 187                     // grab the attributes from the WIN32_FIND_DATA structure
 188                     // (needs to be done while holding closeLock because close
 189                     // will release the buffer)
 190                     attrs = WindowsFileAttributes
 191                         .fromFindData(findDataBuffer.address());
 192                 }
 193 
 194                 // return entry if accepted by filter
 195                 Path entry = acceptEntry(name, attrs);
 196                 if (entry != null)
 197                     return entry;
 198             }
 199         }
 200 
 201         @Override
 202         public synchronized boolean hasNext() {
 203             if (nextEntry == null && !atEof)
 204                 nextEntry = readNextEntry();
 205             return nextEntry != null;
 206         }
 207 
 208         @Override
 209         public synchronized Path next() {
 210             Path result = null;
 211             if (nextEntry == null && !atEof) {
 212                 result = readNextEntry();
 213             } else {
 214                 result = nextEntry;
 215                 nextEntry = null;
 216             }
 217             if (result == null)
 218                 throw new NoSuchElementException();
 219             return result;
 220         }
 221 
 222         @Override
 223         public void remove() {
 224             throw new UnsupportedOperationException();
 225         }
 226     }
 227 }