Path: blob/master/src/java.base/unix/classes/sun/nio/fs/UnixUserDefinedFileAttributeView.java
41137 views
/*1* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.fs;2627import java.lang.ref.Reference;28import java.nio.file.*;29import java.nio.ByteBuffer;30import java.io.IOException;31import java.util.*;32import jdk.internal.misc.Unsafe;3334import static sun.nio.fs.UnixConstants.*;35import static sun.nio.fs.UnixNativeDispatcher.*;3637/**38* Unix implementation of UserDefinedFileAttributeView using extended attributes.39*/40abstract class UnixUserDefinedFileAttributeView41extends AbstractUserDefinedFileAttributeView42{43private static final Unsafe unsafe = Unsafe.getUnsafe();4445// namespace for extended user attributes46private static final String USER_NAMESPACE = "user.";4748private static final int MIN_LISTXATTR_BUF_SIZE = 1024;49private static final int MAX_LISTXATTR_BUF_SIZE = 32 * 1024;5051private final UnixPath file;52private final boolean followLinks;5354UnixUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {55this.file = file;56this.followLinks = followLinks;57}5859private byte[] nameAsBytes(UnixPath file, String name) throws IOException {60if (name == null)61throw new NullPointerException("'name' is null");62name = USER_NAMESPACE + name;63byte[] bytes = Util.toBytes(name);64if (bytes.length > maxNameLength()) {65throw new FileSystemException(file.getPathForExceptionMessage(),66null, "'" + name + "' is too big");67}68return bytes;69}7071/**72* @return the maximum supported length of xattr names (in bytes, including namespace)73*/74protected abstract int maxNameLength();7576// Parses buffer as array of NULL-terminated C strings.77private static List<String> asList(long address, int size) {78List<String> list = new ArrayList<>();79int start = 0;80int pos = 0;81while (pos < size) {82if (unsafe.getByte(address + pos) == 0) {83int len = pos - start;84byte[] value = new byte[len];85unsafe.copyMemory(null, address+start, value,86Unsafe.ARRAY_BYTE_BASE_OFFSET, len);87String s = Util.toString(value);88list.add(s);89start = pos + 1;90}91pos++;92}93return list;94}9596// runs flistxattr, increases buffer size up to MAX_LISTXATTR_BUF_SIZE if required97private static List<String> list(int fd, int bufSize) throws UnixException {98try {99try (NativeBuffer buffer = NativeBuffers.getNativeBuffer(bufSize)) {100int n = flistxattr(fd, buffer.address(), bufSize);101return asList(buffer.address(), n);102} // release buffer before recursion103} catch (UnixException x) {104if (x.errno() == ERANGE && bufSize < MAX_LISTXATTR_BUF_SIZE) {105return list(fd, bufSize * 2); // try larger buffer size:106} else {107throw x;108}109}110}111112@SuppressWarnings("removal")113@Override114public List<String> list() throws IOException {115if (System.getSecurityManager() != null)116checkAccess(file.getPathForPermissionCheck(), true, false);117118int fd = -1;119try {120fd = file.openForAttributeAccess(followLinks);121} catch (UnixException x) {122x.rethrowAsIOException(file);123}124try {125List<String> attrNames = list(fd, MIN_LISTXATTR_BUF_SIZE);126return attrNames.stream()127.filter(s -> s.startsWith(USER_NAMESPACE))128.map(s -> s.substring(USER_NAMESPACE.length()))129.toList();130} catch (UnixException x) {131throw new FileSystemException(file.getPathForExceptionMessage(),132null, "Unable to get list of extended attributes: " +133x.getMessage());134} finally {135close(fd);136}137}138139@SuppressWarnings("removal")140@Override141public int size(String name) throws IOException {142if (System.getSecurityManager() != null)143checkAccess(file.getPathForPermissionCheck(), true, false);144145int fd = -1;146try {147fd = file.openForAttributeAccess(followLinks);148} catch (UnixException x) {149x.rethrowAsIOException(file);150}151try {152// fgetxattr returns size if called with size==0153return fgetxattr(fd, nameAsBytes(file,name), 0L, 0);154} catch (UnixException x) {155throw new FileSystemException(file.getPathForExceptionMessage(),156null, "Unable to get size of extended attribute '" + name +157"': " + x.getMessage());158} finally {159close(fd);160}161}162163@SuppressWarnings("removal")164@Override165public int read(String name, ByteBuffer dst) throws IOException {166if (System.getSecurityManager() != null)167checkAccess(file.getPathForPermissionCheck(), true, false);168169if (dst.isReadOnly())170throw new IllegalArgumentException("Read-only buffer");171int pos = dst.position();172int lim = dst.limit();173assert (pos <= lim);174int rem = (pos <= lim ? lim - pos : 0);175176if (dst instanceof sun.nio.ch.DirectBuffer buf) {177try {178long address = buf.address() + pos;179int n = read(name, address, rem);180dst.position(pos + n);181return n;182} finally {183Reference.reachabilityFence(buf);184}185} else {186try (NativeBuffer nb = NativeBuffers.getNativeBuffer(rem)) {187long address = nb.address();188int n = read(name, address, rem);189190// copy from buffer into backing array191int off = dst.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;192unsafe.copyMemory(null, address, dst.array(), off, n);193dst.position(pos + n);194195return n;196}197}198}199200private int read(String name, long address, int rem) throws IOException {201int fd = -1;202try {203fd = file.openForAttributeAccess(followLinks);204} catch (UnixException x) {205x.rethrowAsIOException(file);206}207try {208int n = fgetxattr(fd, nameAsBytes(file, name), address, rem);209210// if remaining is zero then fgetxattr returns the size211if (rem == 0) {212if (n > 0)213throw new UnixException(ERANGE);214return 0;215}216return n;217} catch (UnixException x) {218String msg = (x.errno() == ERANGE) ?219"Insufficient space in buffer" : x.getMessage();220throw new FileSystemException(file.getPathForExceptionMessage(),221null, "Error reading extended attribute '" + name + "': " + msg);222} finally {223close(fd);224}225}226227@SuppressWarnings("removal")228@Override229public int write(String name, ByteBuffer src) throws IOException {230if (System.getSecurityManager() != null)231checkAccess(file.getPathForPermissionCheck(), false, true);232233int pos = src.position();234int lim = src.limit();235assert (pos <= lim);236int rem = (pos <= lim ? lim - pos : 0);237238if (src instanceof sun.nio.ch.DirectBuffer buf) {239try {240long address = buf.address() + pos;241write(name, address, rem);242src.position(pos + rem);243return rem;244} finally {245Reference.reachabilityFence(buf);246}247} else {248try (NativeBuffer nb = NativeBuffers.getNativeBuffer(rem)) {249long address = nb.address();250251if (src.hasArray()) {252// copy from backing array into buffer253int off = src.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;254unsafe.copyMemory(src.array(), off, null, address, rem);255} else {256// backing array not accessible so transfer via temporary array257byte[] tmp = new byte[rem];258src.get(tmp);259src.position(pos); // reset position as write may fail260unsafe.copyMemory(tmp, Unsafe.ARRAY_BYTE_BASE_OFFSET, null,261address, rem);262}263264write(name, address, rem);265src.position(pos + rem);266return rem;267}268}269}270271private void write(String name, long address, int rem) throws IOException {272int fd = -1;273try {274fd = file.openForAttributeAccess(followLinks);275} catch (UnixException x) {276x.rethrowAsIOException(file);277}278try {279fsetxattr(fd, nameAsBytes(file,name), address, rem);280} catch (UnixException x) {281throw new FileSystemException(file.getPathForExceptionMessage(),282null, "Error writing extended attribute '" + name + "': " +283x.getMessage());284} finally {285close(fd);286}287}288289@SuppressWarnings("removal")290@Override291public void delete(String name) throws IOException {292if (System.getSecurityManager() != null)293checkAccess(file.getPathForPermissionCheck(), false, true);294295int fd = -1;296try {297fd = file.openForAttributeAccess(followLinks);298} catch (UnixException x) {299x.rethrowAsIOException(file);300}301try {302fremovexattr(fd, nameAsBytes(file,name));303} catch (UnixException x) {304throw new FileSystemException(file.getPathForExceptionMessage(),305null, "Unable to delete extended attribute '" + name + "': " + x.getMessage());306} finally {307close(fd);308}309}310311/**312* Used by copyTo/moveTo to copy extended attributes from source to target.313*314* @param ofd315* file descriptor for source file316* @param nfd317* file descriptor for target file318*/319static void copyExtendedAttributes(int ofd, int nfd) {320try {321List<String> attrNames = list(ofd, MIN_LISTXATTR_BUF_SIZE);322for (String name : attrNames) {323try {324copyExtendedAttribute(ofd, Util.toBytes(name), nfd);325} catch(UnixException ignore){326// ignore327}328}329} catch (UnixException e) {330// unable to get list of attributes331return;332}333}334335private static void copyExtendedAttribute(int ofd, byte[] name, int nfd)336throws UnixException337{338int size = fgetxattr(ofd, name, 0L, 0);339NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);340try {341long address = buffer.address();342size = fgetxattr(ofd, name, address, size);343fsetxattr(nfd, name, address, size);344} finally {345buffer.release();346}347}348}349350