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.ByteBuffer; 30 import java.io.IOException; 31 import java.util.*; 32 import sun.misc.Unsafe; 33 34 import static sun.nio.fs.UnixConstants.*; 35 import static sun.nio.fs.LinuxNativeDispatcher.*; 36 37 /** 38 * Linux implementation of UserDefinedFileAttributeView using extended attributes. 39 */ 40 41 class LinuxUserDefinedFileAttributeView 42 extends AbstractUserDefinedFileAttributeView 43 { 44 private static final Unsafe unsafe = Unsafe.getUnsafe(); 45 46 // namespace for extended user attributes 47 private static final String USER_NAMESPACE = "user."; 48 49 // maximum bytes in extended attribute name (includes namespace) 50 private static final int XATTR_NAME_MAX = 255; 51 52 private byte[] nameAsBytes(UnixPath file, String name) throws IOException { 53 if (name == null) 54 throw new NullPointerException("'name' is null"); 55 name = USER_NAMESPACE + name; 56 byte[] bytes = name.getBytes(); 57 if (bytes.length > XATTR_NAME_MAX) { 58 throw new FileSystemException(file.getPathForExecptionMessage(), 59 null, "'" + name + "' is too big"); 60 } 61 return bytes; 62 } 63 64 // Parses buffer as array of NULL-terminated C strings. 65 private List<String> asList(long address, int size) { 66 final List<String> list = new ArrayList<String>(); 67 int start = 0; 68 int pos = 0; 69 while (pos < size) { 70 if (unsafe.getByte(address + pos) == 0) { 71 int len = pos - start; 72 byte[] value = new byte[len]; 73 unsafe.copyMemory(null, address+start, value, 74 Unsafe.ARRAY_BYTE_BASE_OFFSET, len); 75 String s = new String(value); 76 if (s.startsWith(USER_NAMESPACE)) { 77 s = s.substring(USER_NAMESPACE.length()); 78 list.add(s); 79 } 80 start = pos + 1; 81 } 82 pos++; 83 } 84 return list; 85 } 86 87 private final UnixPath file; 88 private final boolean followLinks; 89 90 LinuxUserDefinedFileAttributeView(UnixPath file, boolean followLinks) { 91 this.file = file; 92 this.followLinks = followLinks; 93 } 94 95 @Override 96 public List<String> list() throws IOException { 97 if (System.getSecurityManager() != null) 98 checkAccess(file.getPathForPermissionCheck(), true, false); 99 100 int fd = file.openForAttributeAccess(followLinks); 101 NativeBuffer buffer = null; 102 try { 103 int size = 1024; 104 buffer = NativeBuffers.getNativeBuffer(size); 105 for (;;) { 106 try { 107 int n = flistxattr(fd, buffer.address(), size); 108 List<String> list = asList(buffer.address(), n); 109 return Collections.unmodifiableList(list); 110 } catch (UnixException x) { 111 // allocate larger buffer if required 112 if (x.errno() == ERANGE && size < 32*1024) { 113 buffer.release(); 114 size *= 2; 115 buffer = null; 116 buffer = NativeBuffers.getNativeBuffer(size); 117 continue; 118 } 119 throw new FileSystemException(file.getPathForExecptionMessage(), 120 null, "Unable to get list of extended attributes: " + 121 x.getMessage()); 122 } 123 } 124 } finally { 125 if (buffer != null) 126 buffer.release(); 127 close(fd); 128 } 129 } 130 131 @Override 132 public int size(String name) throws IOException { 133 if (System.getSecurityManager() != null) 134 checkAccess(file.getPathForPermissionCheck(), true, false); 135 136 int fd = file.openForAttributeAccess(followLinks); 137 try { 138 // fgetxattr returns size if called with size==0 139 return fgetxattr(fd, nameAsBytes(file,name), 0L, 0); 140 } catch (UnixException x) { 141 throw new FileSystemException(file.getPathForExecptionMessage(), 142 null, "Unable to get size of extended attribute '" + name + 143 "': " + x.getMessage()); 144 } finally { 145 close(fd); 146 } 147 } 148 149 @Override 150 public int read(String name, ByteBuffer dst) throws IOException { 151 if (System.getSecurityManager() != null) 152 checkAccess(file.getPathForPermissionCheck(), true, false); 153 154 if (dst.isReadOnly()) 155 throw new IllegalArgumentException("Read-only buffer"); 156 int pos = dst.position(); 157 int lim = dst.limit(); 158 assert (pos <= lim); 159 int rem = (pos <= lim ? lim - pos : 0); 160 161 NativeBuffer nb; 162 long address; 163 if (dst instanceof sun.nio.ch.DirectBuffer) { 164 nb = null; 165 address = ((sun.nio.ch.DirectBuffer)dst).address() + pos; 166 } else { 167 // substitute with native buffer 168 nb = NativeBuffers.getNativeBuffer(rem); 169 address = nb.address(); 170 } 171 172 int fd = file.openForAttributeAccess(followLinks); 173 try { 174 try { 175 int n = fgetxattr(fd, nameAsBytes(file,name), address, rem); 176 177 // if remaining is zero then fgetxattr returns the size 178 if (rem == 0) { 179 if (n > 0) 180 throw new UnixException(ERANGE); 181 return 0; 182 } 183 184 // copy from buffer into backing array if necessary 185 if (nb != null) { 186 int off = dst.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET; 187 unsafe.copyMemory(null, address, dst.array(), off, n); 188 } 189 dst.position(pos + n); 190 return n; 191 } catch (UnixException x) { 192 String msg = (x.errno() == ERANGE) ? 193 "Insufficient space in buffer" : x.getMessage(); 194 throw new FileSystemException(file.getPathForExecptionMessage(), 195 null, "Error reading extended attribute '" + name + "': " + msg); 196 } finally { 197 close(fd); 198 } 199 } finally { 200 if (nb != null) 201 nb.release(); 202 } 203 } 204 205 @Override 206 public int write(String name, ByteBuffer src) throws IOException { 207 if (System.getSecurityManager() != null) 208 checkAccess(file.getPathForPermissionCheck(), false, true); 209 210 int pos = src.position(); 211 int lim = src.limit(); 212 assert (pos <= lim); 213 int rem = (pos <= lim ? lim - pos : 0); 214 215 NativeBuffer nb; 216 long address; 217 if (src instanceof sun.nio.ch.DirectBuffer) { 218 nb = null; 219 address = ((sun.nio.ch.DirectBuffer)src).address() + pos; 220 } else { 221 // substitute with native buffer 222 nb = NativeBuffers.getNativeBuffer(rem); 223 address = nb.address(); 224 225 if (src.hasArray()) { 226 // copy from backing array into buffer 227 int off = src.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET; 228 unsafe.copyMemory(src.array(), off, null, address, rem); 229 } else { 230 // backing array not accessible so transfer via temporary array 231 byte[] tmp = new byte[rem]; 232 src.get(tmp); 233 src.position(pos); // reset position as write may fail 234 unsafe.copyMemory(tmp, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, 235 address, rem); 236 } 237 } 238 239 int fd = file.openForAttributeAccess(followLinks); 240 try { 241 try { 242 fsetxattr(fd, nameAsBytes(file,name), address, rem); 243 src.position(pos + rem); 244 return rem; 245 } catch (UnixException x) { 246 throw new FileSystemException(file.getPathForExecptionMessage(), 247 null, "Error writing extended attribute '" + name + "': " + 248 x.getMessage()); 249 } finally { 250 close(fd); 251 } 252 } finally { 253 if (nb != null) 254 nb.release(); 255 } 256 } 257 258 @Override 259 public void delete(String name) throws IOException { 260 if (System.getSecurityManager() != null) 261 checkAccess(file.getPathForPermissionCheck(), false, true); 262 263 int fd = file.openForAttributeAccess(followLinks); 264 try { 265 fremovexattr(fd, nameAsBytes(file,name)); 266 } catch (UnixException x) { 267 throw new FileSystemException(file.getPathForExecptionMessage(), 268 null, "Unable to delete extended attribute '" + name + "': " + x.getMessage()); 269 } finally { 270 close(fd); 271 } 272 } 273 274 /** 275 * Used by copyTo/moveTo to copy extended attributes from source to target. 276 * 277 * @param ofd 278 * file descriptor for source file 279 * @param nfd 280 * file descriptor for target file 281 */ 282 static void copyExtendedAttributes(int ofd, int nfd) { 283 NativeBuffer buffer = null; 284 try { 285 286 // call flistxattr to get list of extended attributes. 287 int size = 1024; 288 buffer = NativeBuffers.getNativeBuffer(size); 289 for (;;) { 290 try { 291 size = flistxattr(ofd, buffer.address(), size); 292 break; 293 } catch (UnixException x) { 294 // allocate larger buffer if required 295 if (x.errno() == ERANGE && size < 32*1024) { 296 buffer.release(); 297 size *= 2; 298 buffer = null; 299 buffer = NativeBuffers.getNativeBuffer(size); 300 continue; 301 } 302 303 // unable to get list of attributes 304 return; 305 } 306 } 307 308 // parse buffer as array of NULL-terminated C strings. 309 long address = buffer.address(); 310 int start = 0; 311 int pos = 0; 312 while (pos < size) { 313 if (unsafe.getByte(address + pos) == 0) { 314 // extract attribute name and copy attribute to target. 315 // FIXME: We can avoid needless copying by using address+pos 316 // as the address of the name. 317 int len = pos - start; 318 byte[] name = new byte[len]; 319 unsafe.copyMemory(null, address+start, name, 320 Unsafe.ARRAY_BYTE_BASE_OFFSET, len); 321 try { 322 copyExtendedAttribute(ofd, name, nfd); 323 } catch (UnixException ignore) { 324 // ignore 325 } 326 start = pos + 1; 327 } 328 pos++; 329 } 330 331 } finally { 332 if (buffer != null) 333 buffer.release(); 334 } 335 } 336 337 private static void copyExtendedAttribute(int ofd, byte[] name, int nfd) 338 throws UnixException 339 { 340 int size = fgetxattr(ofd, name, 0L, 0); 341 NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); 342 try { 343 long address = buffer.address(); 344 size = fgetxattr(ofd, name, address, size); 345 fsetxattr(nfd, name, address, size); 346 } finally { 347 buffer.release(); 348 } 349 } 350 }