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