| /* |
| * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.nio.fs; |
| |
| import java.nio.file.*; |
| import java.nio.ByteBuffer; |
| import java.io.IOException; |
| import java.util.*; |
| import jdk.internal.misc.Unsafe; |
| |
| import static sun.nio.fs.UnixConstants.*; |
| import static sun.nio.fs.LinuxNativeDispatcher.*; |
| |
| /** |
| * Linux implementation of UserDefinedFileAttributeView using extended attributes. |
| */ |
| |
| class LinuxUserDefinedFileAttributeView |
| extends AbstractUserDefinedFileAttributeView |
| { |
| private static final Unsafe unsafe = Unsafe.getUnsafe(); |
| |
| // namespace for extended user attributes |
| private static final String USER_NAMESPACE = "user."; |
| |
| // maximum bytes in extended attribute name (includes namespace) |
| private static final int XATTR_NAME_MAX = 255; |
| |
| private byte[] nameAsBytes(UnixPath file, String name) throws IOException { |
| if (name == null) |
| throw new NullPointerException("'name' is null"); |
| name = USER_NAMESPACE + name; |
| byte[] bytes = Util.toBytes(name); |
| if (bytes.length > XATTR_NAME_MAX) { |
| throw new FileSystemException(file.getPathForExceptionMessage(), |
| null, "'" + name + "' is too big"); |
| } |
| return bytes; |
| } |
| |
| // Parses buffer as array of NULL-terminated C strings. |
| private List<String> asList(long address, int size) { |
| List<String> list = new ArrayList<>(); |
| int start = 0; |
| int pos = 0; |
| while (pos < size) { |
| if (unsafe.getByte(address + pos) == 0) { |
| int len = pos - start; |
| byte[] value = new byte[len]; |
| unsafe.copyMemory(null, address+start, value, |
| Unsafe.ARRAY_BYTE_BASE_OFFSET, len); |
| String s = Util.toString(value); |
| if (s.startsWith(USER_NAMESPACE)) { |
| s = s.substring(USER_NAMESPACE.length()); |
| list.add(s); |
| } |
| start = pos + 1; |
| } |
| pos++; |
| } |
| return list; |
| } |
| |
| private final UnixPath file; |
| private final boolean followLinks; |
| |
| LinuxUserDefinedFileAttributeView(UnixPath file, boolean followLinks) { |
| this.file = file; |
| this.followLinks = followLinks; |
| } |
| |
| @Override |
| public List<String> list() throws IOException { |
| if (System.getSecurityManager() != null) |
| checkAccess(file.getPathForPermissionCheck(), true, false); |
| |
| int fd = -1; |
| try { |
| fd = file.openForAttributeAccess(followLinks); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } |
| NativeBuffer buffer = null; |
| try { |
| int size = 1024; |
| buffer = NativeBuffers.getNativeBuffer(size); |
| for (;;) { |
| try { |
| int n = flistxattr(fd, buffer.address(), size); |
| List<String> list = asList(buffer.address(), n); |
| return Collections.unmodifiableList(list); |
| } catch (UnixException x) { |
| // allocate larger buffer if required |
| if (x.errno() == ERANGE && size < 32*1024) { |
| buffer.release(); |
| size *= 2; |
| buffer = null; |
| buffer = NativeBuffers.getNativeBuffer(size); |
| continue; |
| } |
| throw new FileSystemException(file.getPathForExceptionMessage(), |
| null, "Unable to get list of extended attributes: " + |
| x.getMessage()); |
| } |
| } |
| } finally { |
| if (buffer != null) |
| buffer.release(); |
| close(fd); |
| } |
| } |
| |
| @Override |
| public int size(String name) throws IOException { |
| if (System.getSecurityManager() != null) |
| checkAccess(file.getPathForPermissionCheck(), true, false); |
| |
| int fd = -1; |
| try { |
| fd = file.openForAttributeAccess(followLinks); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } |
| try { |
| // fgetxattr returns size if called with size==0 |
| return fgetxattr(fd, nameAsBytes(file,name), 0L, 0); |
| } catch (UnixException x) { |
| throw new FileSystemException(file.getPathForExceptionMessage(), |
| null, "Unable to get size of extended attribute '" + name + |
| "': " + x.getMessage()); |
| } finally { |
| close(fd); |
| } |
| } |
| |
| @Override |
| public int read(String name, ByteBuffer dst) throws IOException { |
| if (System.getSecurityManager() != null) |
| checkAccess(file.getPathForPermissionCheck(), true, false); |
| |
| if (dst.isReadOnly()) |
| throw new IllegalArgumentException("Read-only buffer"); |
| int pos = dst.position(); |
| int lim = dst.limit(); |
| assert (pos <= lim); |
| int rem = (pos <= lim ? lim - pos : 0); |
| |
| NativeBuffer nb; |
| long address; |
| if (dst instanceof sun.nio.ch.DirectBuffer) { |
| nb = null; |
| address = ((sun.nio.ch.DirectBuffer)dst).address() + pos; |
| } else { |
| // substitute with native buffer |
| nb = NativeBuffers.getNativeBuffer(rem); |
| address = nb.address(); |
| } |
| |
| int fd = -1; |
| try { |
| fd = file.openForAttributeAccess(followLinks); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } |
| try { |
| try { |
| int n = fgetxattr(fd, nameAsBytes(file,name), address, rem); |
| |
| // if remaining is zero then fgetxattr returns the size |
| if (rem == 0) { |
| if (n > 0) |
| throw new UnixException(ERANGE); |
| return 0; |
| } |
| |
| // copy from buffer into backing array if necessary |
| if (nb != null) { |
| int off = dst.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET; |
| unsafe.copyMemory(null, address, dst.array(), off, n); |
| } |
| dst.position(pos + n); |
| return n; |
| } catch (UnixException x) { |
| String msg = (x.errno() == ERANGE) ? |
| "Insufficient space in buffer" : x.getMessage(); |
| throw new FileSystemException(file.getPathForExceptionMessage(), |
| null, "Error reading extended attribute '" + name + "': " + msg); |
| } finally { |
| close(fd); |
| } |
| } finally { |
| if (nb != null) |
| nb.release(); |
| } |
| } |
| |
| @Override |
| public int write(String name, ByteBuffer src) throws IOException { |
| if (System.getSecurityManager() != null) |
| checkAccess(file.getPathForPermissionCheck(), false, true); |
| |
| int pos = src.position(); |
| int lim = src.limit(); |
| assert (pos <= lim); |
| int rem = (pos <= lim ? lim - pos : 0); |
| |
| NativeBuffer nb; |
| long address; |
| if (src instanceof sun.nio.ch.DirectBuffer) { |
| nb = null; |
| address = ((sun.nio.ch.DirectBuffer)src).address() + pos; |
| } else { |
| // substitute with native buffer |
| nb = NativeBuffers.getNativeBuffer(rem); |
| address = nb.address(); |
| |
| if (src.hasArray()) { |
| // copy from backing array into buffer |
| int off = src.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET; |
| unsafe.copyMemory(src.array(), off, null, address, rem); |
| } else { |
| // backing array not accessible so transfer via temporary array |
| byte[] tmp = new byte[rem]; |
| src.get(tmp); |
| src.position(pos); // reset position as write may fail |
| unsafe.copyMemory(tmp, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, |
| address, rem); |
| } |
| } |
| |
| int fd = -1; |
| try { |
| fd = file.openForAttributeAccess(followLinks); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } |
| try { |
| try { |
| fsetxattr(fd, nameAsBytes(file,name), address, rem); |
| src.position(pos + rem); |
| return rem; |
| } catch (UnixException x) { |
| throw new FileSystemException(file.getPathForExceptionMessage(), |
| null, "Error writing extended attribute '" + name + "': " + |
| x.getMessage()); |
| } finally { |
| close(fd); |
| } |
| } finally { |
| if (nb != null) |
| nb.release(); |
| } |
| } |
| |
| @Override |
| public void delete(String name) throws IOException { |
| if (System.getSecurityManager() != null) |
| checkAccess(file.getPathForPermissionCheck(), false, true); |
| |
| int fd = -1; |
| try { |
| fd = file.openForAttributeAccess(followLinks); |
| } catch (UnixException x) { |
| x.rethrowAsIOException(file); |
| } |
| try { |
| fremovexattr(fd, nameAsBytes(file,name)); |
| } catch (UnixException x) { |
| throw new FileSystemException(file.getPathForExceptionMessage(), |
| null, "Unable to delete extended attribute '" + name + "': " + x.getMessage()); |
| } finally { |
| close(fd); |
| } |
| } |
| |
| /** |
| * Used by copyTo/moveTo to copy extended attributes from source to target. |
| * |
| * @param ofd |
| * file descriptor for source file |
| * @param nfd |
| * file descriptor for target file |
| */ |
| static void copyExtendedAttributes(int ofd, int nfd) { |
| NativeBuffer buffer = null; |
| try { |
| |
| // call flistxattr to get list of extended attributes. |
| int size = 1024; |
| buffer = NativeBuffers.getNativeBuffer(size); |
| for (;;) { |
| try { |
| size = flistxattr(ofd, buffer.address(), size); |
| break; |
| } catch (UnixException x) { |
| // allocate larger buffer if required |
| if (x.errno() == ERANGE && size < 32*1024) { |
| buffer.release(); |
| size *= 2; |
| buffer = null; |
| buffer = NativeBuffers.getNativeBuffer(size); |
| continue; |
| } |
| |
| // unable to get list of attributes |
| return; |
| } |
| } |
| |
| // parse buffer as array of NULL-terminated C strings. |
| long address = buffer.address(); |
| int start = 0; |
| int pos = 0; |
| while (pos < size) { |
| if (unsafe.getByte(address + pos) == 0) { |
| // extract attribute name and copy attribute to target. |
| // FIXME: We can avoid needless copying by using address+pos |
| // as the address of the name. |
| int len = pos - start; |
| byte[] name = new byte[len]; |
| unsafe.copyMemory(null, address+start, name, |
| Unsafe.ARRAY_BYTE_BASE_OFFSET, len); |
| try { |
| copyExtendedAttribute(ofd, name, nfd); |
| } catch (UnixException ignore) { |
| // ignore |
| } |
| start = pos + 1; |
| } |
| pos++; |
| } |
| |
| } finally { |
| if (buffer != null) |
| buffer.release(); |
| } |
| } |
| |
| private static void copyExtendedAttribute(int ofd, byte[] name, int nfd) |
| throws UnixException |
| { |
| int size = fgetxattr(ofd, name, 0L, 0); |
| NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); |
| try { |
| long address = buffer.address(); |
| size = fgetxattr(ofd, name, address, size); |
| fsetxattr(nfd, name, address, size); |
| } finally { |
| buffer.release(); |
| } |
| } |
| } |