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 26package sun.nio.fs; 27 28import java.nio.file.*; 29import static java.nio.file.StandardOpenOption.*; 30import java.nio.ByteBuffer; 31import java.nio.channels.FileChannel; 32import java.io.IOException; 33import java.util.*; 34import jdk.internal.misc.Unsafe; 35 36import static sun.nio.fs.WindowsNativeDispatcher.*; 37import static sun.nio.fs.WindowsConstants.*; 38 39/** 40 * Windows emulation of NamedAttributeView using Alternative Data Streams 41 */ 42 43class 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<>(); 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 @Override 95 public List<String> list() throws IOException { 96 if (System.getSecurityManager() != null) 97 checkAccess(file.getPathForPermissionCheck(), true, false); 98 return listUsingStreamEnumeration(); 99 } 100 101 @Override 102 public int size(String name) throws IOException { 103 if (System.getSecurityManager() != null) 104 checkAccess(file.getPathForPermissionCheck(), true, false); 105 106 // wrap with channel 107 FileChannel fc = null; 108 try { 109 Set<OpenOption> opts = new HashSet<>(); 110 opts.add(READ); 111 if (!followLinks) 112 opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); 113 fc = WindowsChannelFactory 114 .newFileChannel(join(file, name), null, opts, 0L); 115 } catch (WindowsException x) { 116 x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); 117 } 118 try { 119 long size = fc.size(); 120 if (size > Integer.MAX_VALUE) 121 throw new ArithmeticException("Stream too large"); 122 return (int)size; 123 } finally { 124 fc.close(); 125 } 126 } 127 128 @Override 129 public int read(String name, ByteBuffer dst) throws IOException { 130 if (System.getSecurityManager() != null) 131 checkAccess(file.getPathForPermissionCheck(), true, false); 132 133 // wrap with channel 134 FileChannel fc = null; 135 try { 136 Set<OpenOption> opts = new HashSet<>(); 137 opts.add(READ); 138 if (!followLinks) 139 opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); 140 fc = WindowsChannelFactory 141 .newFileChannel(join(file, name), null, opts, 0L); 142 } catch (WindowsException x) { 143 x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); 144 } 145 146 // read to EOF (nothing we can do if I/O error occurs) 147 try { 148 if (fc.size() > dst.remaining()) 149 throw new IOException("Stream too large"); 150 int total = 0; 151 while (dst.hasRemaining()) { 152 int n = fc.read(dst); 153 if (n < 0) 154 break; 155 total += n; 156 } 157 return total; 158 } finally { 159 fc.close(); 160 } 161 } 162 163 @Override 164 public int write(String name, ByteBuffer src) throws IOException { 165 if (System.getSecurityManager() != null) 166 checkAccess(file.getPathForPermissionCheck(), false, true); 167 168 /** 169 * Creating a named stream will cause the unnamed stream to be created 170 * if it doesn't already exist. To avoid this we open the unnamed stream 171 * for reading and hope it isn't deleted/moved while we create or 172 * replace the named stream. Opening the file without sharing options 173 * may cause sharing violations with other programs that are accessing 174 * the unnamed stream. 175 */ 176 long handle = -1L; 177 try { 178 int flags = FILE_FLAG_BACKUP_SEMANTICS; 179 if (!followLinks) 180 flags |= FILE_FLAG_OPEN_REPARSE_POINT; 181 182 handle = CreateFile(file.getPathForWin32Calls(), 183 GENERIC_READ, 184 (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), 185 OPEN_EXISTING, 186 flags); 187 } catch (WindowsException x) { 188 x.rethrowAsIOException(file); 189 } 190 try { 191 Set<OpenOption> opts = new HashSet<>(); 192 if (!followLinks) 193 opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); 194 opts.add(CREATE); 195 opts.add(WRITE); 196 opts.add(StandardOpenOption.TRUNCATE_EXISTING); 197 FileChannel named = null; 198 try { 199 named = WindowsChannelFactory 200 .newFileChannel(join(file, name), null, opts, 0L); 201 } catch (WindowsException x) { 202 x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); 203 } 204 // write value (nothing we can do if I/O error occurs) 205 try { 206 int rem = src.remaining(); 207 while (src.hasRemaining()) { 208 named.write(src); 209 } 210 return rem; 211 } finally { 212 named.close(); 213 } 214 } finally { 215 CloseHandle(handle); 216 } 217 } 218 219 @Override 220 public void delete(String name) throws IOException { 221 if (System.getSecurityManager() != null) 222 checkAccess(file.getPathForPermissionCheck(), false, true); 223 224 String path = WindowsLinkSupport.getFinalPath(file, followLinks); 225 String toDelete = join(path, name); 226 try { 227 DeleteFile(toDelete); 228 } catch (WindowsException x) { 229 x.rethrowAsIOException(toDelete); 230 } 231 } 232} 233