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