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 static java.nio.file.StandardOpenOption.*; 30 import java.nio.ByteBuffer; 31 import java.nio.channels.FileChannel; 32 import java.io.IOException; 33 import java.util.*; 34 import sun.misc.Unsafe; 35 36 import static sun.nio.fs.WindowsNativeDispatcher.*; 37 import static sun.nio.fs.WindowsConstants.*; 38 39 /** 40 * Windows emulation of NamedAttributeView using Alternative Data Streams 41 */ 42 43 class WindowsUserDefinedFileAttributeView 44 extends AbstractUserDefinedFileAttributeView 45 { 46 private static final Unsafe unsafe = Unsafe.getUnsafe(); 47 48 // syntax to address named streams 49 private String join(String file, String name) { 50 if (name == null) 51 throw new NullPointerException("'name' is null"); 52 return file + ":" + name; 53 } 54 private String join(WindowsPath file, String name) throws WindowsException { 55 return join(file.getPathForWin32Calls(), name); 56 } 57 58 private final WindowsPath file; 59 private final boolean followLinks; 60 61 WindowsUserDefinedFileAttributeView(WindowsPath file, boolean followLinks) { 62 this.file = file; 63 this.followLinks = followLinks; 64 } 65 66 // enumerates the file streams using FindFirstStream/FindNextStream APIs. 67 private List<String> listUsingStreamEnumeration() throws IOException { 68 List<String> list = new ArrayList<String>(); 69 try { 70 FirstStream first = FindFirstStream(file.getPathForWin32Calls()); 71 if (first != null) { 72 long handle = first.handle(); 73 try { 74 // first stream is always ::$DATA for files 75 String name = first.name(); 76 if (!name.equals("::$DATA")) { 77 String[] segs = name.split(":"); 78 list.add(segs[1]); 79 } 80 while ((name = FindNextStream(handle)) != null) { 81 String[] segs = name.split(":"); 82 list.add(segs[1]); 83 } 84 } finally { 85 FindClose(handle); 86 } 87 } 88 } catch (WindowsException x) { 89 x.rethrowAsIOException(file); 90 } 91 return Collections.unmodifiableList(list); 92 } 93 94 // enumerates the file streams by reading the stream headers using 95 // BackupRead 96 private List<String> listUsingBackupRead() throws IOException { 97 long handle = -1L; 98 try { 99 int flags = FILE_FLAG_BACKUP_SEMANTICS; 100 if (!followLinks && file.getFileSystem().supportsLinks()) 101 flags |= FILE_FLAG_OPEN_REPARSE_POINT; 102 103 handle = CreateFile(file.getPathForWin32Calls(), 104 GENERIC_READ, 105 FILE_SHARE_READ, // no write as we depend on file size 106 OPEN_EXISTING, 107 flags); 108 } catch (WindowsException x) { 109 x.rethrowAsIOException(file); 110 } 111 112 // buffer to read stream header and stream name. 113 final int BUFFER_SIZE = 4096; 114 NativeBuffer buffer = null; 115 116 // result with names of alternative data streams 117 final List<String> list = new ArrayList<String>(); 118 119 try { 120 buffer = NativeBuffers.getNativeBuffer(BUFFER_SIZE); 121 long address = buffer.address(); 122 123 /** 124 * typedef struct _WIN32_STREAM_ID { 125 * DWORD dwStreamId; 126 * DWORD dwStreamAttributes; 127 * LARGE_INTEGER Size; 128 * DWORD dwStreamNameSize; 129 * WCHAR cStreamName[ANYSIZE_ARRAY]; 130 * } WIN32_STREAM_ID; 131 */ 132 final int SIZEOF_STREAM_HEADER = 20; 133 final int OFFSETOF_STREAM_ID = 0; 134 final int OFFSETOF_STREAM_SIZE = 8; 135 final int OFFSETOF_STREAM_NAME_SIZE = 16; 136 137 long context = 0L; 138 try { 139 for (;;) { 140 // read stream header 141 BackupResult result = BackupRead(handle, address, 142 SIZEOF_STREAM_HEADER, false, context); 143 context = result.context(); 144 if (result.bytesTransferred() == 0) 145 break; 146 147 int streamId = unsafe.getInt(address + OFFSETOF_STREAM_ID); 148 long streamSize = unsafe.getLong(address + OFFSETOF_STREAM_SIZE); 149 int nameSize = unsafe.getInt(address + OFFSETOF_STREAM_NAME_SIZE); 150 151 // read stream name 152 if (nameSize > 0) { 153 result = BackupRead(handle, address, nameSize, false, context); 154 if (result.bytesTransferred() != nameSize) 155 break; 156 } 157 158 // check for alternative data stream 159 if (streamId == BACKUP_ALTERNATE_DATA) { 160 char[] nameAsArray = new char[nameSize/2]; 161 unsafe.copyMemory(null, address, nameAsArray, 162 Unsafe.ARRAY_CHAR_BASE_OFFSET, nameSize); 163 164 String[] segs = new String(nameAsArray).split(":"); 165 if (segs.length == 3) 166 list.add(segs[1]); 167 } 168 169 // sparse blocks not currently handled as documentation 170 // is not sufficient on how the spase block can be skipped. 171 if (streamId == BACKUP_SPARSE_BLOCK) { 172 throw new IOException("Spare blocks not handled"); 173 } 174 175 // seek to end of stream 176 if (streamSize > 0L) { 177 BackupSeek(handle, streamSize, context); 178 } 179 } 180 } catch (WindowsException x) { 181 // failed to read or seek 182 throw new IOException(x.errorString()); 183 } finally { 184 // release context 185 if (context != 0L) { 186 try { 187 BackupRead(handle, 0L, 0, true, context); 188 } catch (WindowsException ignore) { } 189 } 190 } 191 } finally { 192 if (buffer != null) 193 buffer.release(); 194 CloseHandle(handle); 195 } 196 return Collections.unmodifiableList(list); 197 } 198 199 @Override 200 public List<String> list() throws IOException { 201 if (System.getSecurityManager() != null) 202 checkAccess(file.getPathForPermissionCheck(), true, false); 203 // use stream APIs on Windwos Server 2003 and newer 204 if (file.getFileSystem().supportsStreamEnumeration()) { 205 return listUsingStreamEnumeration(); 206 } else { 207 return listUsingBackupRead(); 208 } 209 } 210 211 @Override 212 public int size(String name) throws IOException { 213 if (System.getSecurityManager() != null) 214 checkAccess(file.getPathForPermissionCheck(), true, false); 215 216 // wrap with channel 217 FileChannel fc = null; 218 try { 219 Set<OpenOption> opts = new HashSet<OpenOption>(); 220 opts.add(READ); 221 if (!followLinks) 222 opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); 223 fc = WindowsChannelFactory 224 .newFileChannel(join(file, name), null, opts, 0L); 225 } catch (WindowsException x) { 226 x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); 227 } 228 try { 229 long size = fc.size(); 230 if (size > Integer.MAX_VALUE) 231 throw new ArithmeticException("Stream too large"); 232 return (int)size; 233 } finally { 234 fc.close(); 235 } 236 } 237 238 @Override 239 public int read(String name, ByteBuffer dst) throws IOException { 240 if (System.getSecurityManager() != null) 241 checkAccess(file.getPathForPermissionCheck(), true, false); 242 243 // wrap with channel 244 FileChannel fc = null; 245 try { 246 Set<OpenOption> opts = new HashSet<OpenOption>(); 247 opts.add(READ); 248 if (!followLinks) 249 opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); 250 fc = WindowsChannelFactory 251 .newFileChannel(join(file, name), null, opts, 0L); 252 } catch (WindowsException x) { 253 x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); 254 } 255 256 // read to EOF (nothing we can do if I/O error occurs) 257 try { 258 if (fc.size() > dst.remaining()) 259 throw new IOException("Stream too large"); 260 int total = 0; 261 while (dst.hasRemaining()) { 262 int n = fc.read(dst); 263 if (n < 0) 264 break; 265 total += n; 266 } 267 return total; 268 } finally { 269 fc.close(); 270 } 271 } 272 273 @Override 274 public int write(String name, ByteBuffer src) throws IOException { 275 if (System.getSecurityManager() != null) 276 checkAccess(file.getPathForPermissionCheck(), false, true); 277 278 /** 279 * Creating a named stream will cause the unnamed stream to be created 280 * if it doesn't already exist. To avoid this we open the unnamed stream 281 * for reading and hope it isn't deleted/moved while we create or 282 * replace the named stream. Opening the file without sharing options 283 * may cause sharing violations with other programs that are accessing 284 * the unnamed stream. 285 */ 286 long handle = -1L; 287 try { 288 int flags = FILE_FLAG_BACKUP_SEMANTICS; 289 if (!followLinks) 290 flags |= FILE_FLAG_OPEN_REPARSE_POINT; 291 292 handle = CreateFile(file.getPathForWin32Calls(), 293 GENERIC_READ, 294 (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), 295 OPEN_EXISTING, 296 flags); 297 } catch (WindowsException x) { 298 x.rethrowAsIOException(file); 299 } 300 try { 301 Set<OpenOption> opts = new HashSet<OpenOption>(); 302 if (!followLinks) 303 opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); 304 opts.add(CREATE); 305 opts.add(WRITE); 306 opts.add(StandardOpenOption.TRUNCATE_EXISTING); 307 FileChannel named = null; 308 try { 309 named = WindowsChannelFactory 310 .newFileChannel(join(file, name), null, opts, 0L); 311 } catch (WindowsException x) { 312 x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); 313 } 314 // write value (nothing we can do if I/O error occurs) 315 try { 316 int rem = src.remaining(); 317 while (src.hasRemaining()) { 318 named.write(src); 319 } 320 return rem; 321 } finally { 322 named.close(); 323 } 324 } finally { 325 CloseHandle(handle); 326 } 327 } 328 329 @Override 330 public void delete(String name) throws IOException { 331 if (System.getSecurityManager() != null) 332 checkAccess(file.getPathForPermissionCheck(), false, true); 333 334 String path = WindowsLinkSupport.getFinalPath(file, followLinks); 335 String toDelete = join(path, name); 336 try { 337 DeleteFile(toDelete); 338 } catch (WindowsException x) { 339 x.rethrowAsIOException(toDelete); 340 } 341 } 342 }