1 /* 2 * Copyright (c) 2008, 2014, 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 /* 27 * Portions Copyright (c) 2014 IBM Corporation 28 */ 29 30 package sun.nio.fs; 31 32 import java.nio.file.*; 33 import java.nio.ByteBuffer; 34 import java.io.IOException; 35 import java.util.*; 36 import sun.misc.Unsafe; 37 38 import static sun.nio.fs.UnixConstants.*; 39 import static sun.nio.fs.AixNativeDispatcher.*; 40 41 /** 42 * Aix implementation of UserDefinedFileAttributeView using extended attributes. 43 */ 44 45 class AixUserDefinedFileAttributeView 46 extends AbstractUserDefinedFileAttributeView 47 { 48 private static final Unsafe unsafe = Unsafe.getUnsafe(); 49 50 // namespace for extended user attributes 51 private static final String USER_NAMESPACE = "user."; 52 53 // maximum bytes in extended attribute name (includes namespace) 54 private static final int XATTR_NAME_MAX = 255; 55 56 private byte[] nameAsBytes(UnixPath file, String name) throws IOException { 57 if (name == null) 58 throw new NullPointerException("'name' is null"); 59 name = USER_NAMESPACE + name; 60 byte[] bytes = name.getBytes(); 61 if (bytes.length > XATTR_NAME_MAX) { 62 throw new FileSystemException(file.getPathForExceptionMessage(), 63 null, "'" + name + "' is too big"); 64 } 65 return bytes; 66 } 67 68 // Parses buffer as array of NULL-terminated C strings. 69 private List<String> asList(long address, int size) { 70 final List<String> list = new ArrayList<String>(); 71 int start = 0; 72 int pos = 0; 73 while (pos < size) { 74 if (unsafe.getByte(address + pos) == 0) { 75 int len = pos - start; 76 byte[] value = new byte[len]; 77 unsafe.copyMemory(null, address+start, value, 78 Unsafe.ARRAY_BYTE_BASE_OFFSET, len); 79 String s = new String(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 94 AixUserDefinedFileAttributeView(UnixPath file, boolean followLinks) { 95 this.file = file; 96 this.followLinks = followLinks; 97 } 98 99 @Override 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 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 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 int off = dst.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET; 191 unsafe.copyMemory(null, address, dst.array(), off, n); 192 } 193 dst.position(pos + n); 194 return n; 195 } catch (UnixException x) { 196 String msg = (x.errno() == ERANGE) ? 197 "Insufficient space in buffer" : x.getMessage(); 198 throw new FileSystemException(file.getPathForExceptionMessage(), 199 null, "Error reading extended attribute '" + name + "': " + msg); 200 } finally { 201 close(fd); 202 } 203 } finally { 204 if (nb != null) 205 nb.release(); 206 } 207 } 208 209 @Override 210 public int write(String name, ByteBuffer src) throws IOException { 211 if (System.getSecurityManager() != null) 212 checkAccess(file.getPathForPermissionCheck(), false, true); 213 214 int pos = src.position(); 215 int lim = src.limit(); 216 assert (pos <= lim); 217 int rem = (pos <= lim ? lim - pos : 0); 218 219 NativeBuffer nb; 220 long address; 221 if (src instanceof sun.nio.ch.DirectBuffer) { 222 nb = null; 223 address = ((sun.nio.ch.DirectBuffer)src).address() + pos; 224 } else { 225 // substitute with native buffer 226 nb = NativeBuffers.getNativeBuffer(rem); 227 address = nb.address(); 228 229 if (src.hasArray()) { 230 // copy from backing array into buffer 231 int off = src.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET; 232 unsafe.copyMemory(src.array(), off, null, address, rem); 233 } else { 234 // backing array not accessible so transfer via temporary array 235 byte[] tmp = new byte[rem]; 236 src.get(tmp); 237 src.position(pos); // reset position as write may fail 238 unsafe.copyMemory(tmp, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, 239 address, rem); 240 } 241 } 242 243 int fd = file.openForAttributeAccess(followLinks); 244 try { 245 try { 246 fsetxattr(fd, nameAsBytes(file,name), address, rem); 247 src.position(pos + rem); 248 return rem; 249 } catch (UnixException x) { 250 throw new FileSystemException(file.getPathForExceptionMessage(), 251 null, "Error writing extended attribute '" + name + "': " + 252 x.getMessage()); 253 } finally { 254 close(fd); 255 } 256 } finally { 257 if (nb != null) 258 nb.release(); 259 } 260 } 261 262 @Override 263 public void delete(String name) throws IOException { 264 if (System.getSecurityManager() != null) 265 checkAccess(file.getPathForPermissionCheck(), false, true); 266 267 int fd = file.openForAttributeAccess(followLinks); 268 try { 269 fremovexattr(fd, nameAsBytes(file,name)); 270 } catch (UnixException x) { 271 throw new FileSystemException(file.getPathForExceptionMessage(), 272 null, "Unable to delete extended attribute '" + name + "': " + x.getMessage()); 273 } finally { 274 close(fd); 275 } 276 } 277 278 /** 279 * Used by copyTo/moveTo to copy extended attributes from source to target. 280 * 281 * @param ofd 282 * file descriptor for source file 283 * @param nfd 284 * file descriptor for target file 285 */ 286 static void copyExtendedAttributes(int ofd, int nfd) { 287 NativeBuffer buffer = null; 288 try { 289 290 // call flistxattr to get list of extended attributes. 291 int size = 1024; 292 buffer = NativeBuffers.getNativeBuffer(size); 293 for (;;) { 294 try { 295 size = flistxattr(ofd, buffer.address(), size); 296 break; 297 } catch (UnixException x) { 298 // allocate larger buffer if required 299 if (x.errno() == ERANGE && size < 32*1024) { 300 buffer.release(); 301 size *= 2; 302 buffer = null; 303 buffer = NativeBuffers.getNativeBuffer(size); 304 continue; 305 } 306 307 // unable to get list of attributes 308 return; 309 } 310 } 311 312 // parse buffer as array of NULL-terminated C strings. 313 long address = buffer.address(); 314 int start = 0; 315 int pos = 0; 316 while (pos < size) { 317 if (unsafe.getByte(address + pos) == 0) { 318 // extract attribute name and copy attribute to target. 319 // FIXME: We can avoid needless copying by using address+pos 320 // as the address of the name. 321 int len = pos - start; 322 byte[] name = new byte[len]; 323 unsafe.copyMemory(null, address+start, name, 324 Unsafe.ARRAY_BYTE_BASE_OFFSET, len); 325 try { 326 copyExtendedAttribute(ofd, name, nfd); 327 } catch (UnixException ignore) { 328 // ignore 329 } 330 start = pos + 1; 331 } 332 pos++; 333 } 334 335 } finally { 336 if (buffer != null) 337 buffer.release(); 338 } 339 } 340 341 private static void copyExtendedAttribute(int ofd, byte[] name, int nfd) 342 throws UnixException 343 { 344 int size = fgetxattr(ofd, name, 0L, 0); 345 NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); 346 try { 347 long address = buffer.address(); 348 size = fgetxattr(ofd, name, address, size); 349 fsetxattr(nfd, name, address, size); 350 } finally { 351 buffer.release(); 352 } 353 } 354 }