8260966: (fs) Consolidate Linux and macOS implementations of UserDefinedFileAttributeView
8260691: (fs) LinuxNativeDispatcher should link to xattr functions

Reviewed-by: alanb
diff --git a/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java b/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java
index 0816146..5a8e080 100644
--- a/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java
+++ b/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java
@@ -105,27 +105,6 @@
         throw new IOException("Mount point not found");
     }
 
-    // returns true if extended attributes enabled on file system where given
-    // file resides, returns false if disabled or unable to determine.
-    private boolean isExtendedAttributesEnabled(UnixPath path) {
-        int fd = -1;
-        try {
-            fd = path.openForAttributeAccess(false);
-
-            // fgetxattr returns size if called with size==0
-            byte[] name = Util.toBytes("user.java");
-            LinuxNativeDispatcher.fgetxattr(fd, name, 0L, 0);
-            return true;
-        } catch (UnixException e) {
-            // attribute does not exist
-            if (e.errno() == UnixConstants.ENODATA)
-                return true;
-        } finally {
-            UnixNativeDispatcher.close(fd);
-        }
-        return false;
-    }
-
     // get kernel version as a three element array {major, minor, micro}
     private static int[] getKernelVersion() {
         Pattern pattern = Pattern.compile("\\D+");
diff --git a/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java b/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java
index cfd8396..a3153b3 100644
--- a/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java
+++ b/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java
@@ -69,7 +69,7 @@
 
     @Override
     void copyNonPosixAttributes(int ofd, int nfd) {
-        LinuxUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
+        UnixUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
     }
 
     /**
diff --git a/src/java.base/linux/classes/sun/nio/fs/LinuxNativeDispatcher.java b/src/java.base/linux/classes/sun/nio/fs/LinuxNativeDispatcher.java
index 289dc7c..6e6d59f 100644
--- a/src/java.base/linux/classes/sun/nio/fs/LinuxNativeDispatcher.java
+++ b/src/java.base/linux/classes/sun/nio/fs/LinuxNativeDispatcher.java
@@ -69,61 +69,6 @@
      */
     static native void endmntent(long stream) throws UnixException;
 
-    /**
-     * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
-     */
-    static int fgetxattr(int filedes, byte[] name, long valueAddress,
-                         int valueLen) throws UnixException
-    {
-        NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
-        try {
-            return fgetxattr0(filedes, buffer.address(), valueAddress, valueLen);
-        } finally {
-            buffer.release();
-        }
-    }
-
-    private static native int fgetxattr0(int filedes, long nameAddress,
-        long valueAddress, int valueLen) throws UnixException;
-
-    /**
-     *  fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags);
-     */
-    static void fsetxattr(int filedes, byte[] name, long valueAddress,
-        int valueLen) throws UnixException
-    {
-        NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
-        try {
-            fsetxattr0(filedes, buffer.address(), valueAddress, valueLen);
-        } finally {
-            buffer.release();
-        }
-    }
-
-    private static native void fsetxattr0(int filedes, long nameAddress,
-        long valueAddress, int valueLen) throws UnixException;
-
-    /**
-     * fremovexattr(int filedes, const char *name);
-     */
-    static void fremovexattr(int filedes, byte[] name) throws UnixException {
-        NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
-        try {
-            fremovexattr0(filedes, buffer.address());
-        } finally {
-            buffer.release();
-        }
-    }
-
-    private static native void fremovexattr0(int filedes, long nameAddress)
-        throws UnixException;
-
-    /**
-     * size_t flistxattr(int filedes, const char *list, size_t size)
-     */
-    static native int flistxattr(int filedes, long listAddress, int size)
-        throws UnixException;
-
     // initialize
     private static native void init();
 
diff --git a/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java b/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java
index 568341b..3175c90 100644
--- a/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java
+++ b/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java
@@ -25,351 +25,17 @@
 
 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
+    extends UnixUserDefinedFileAttributeView
 {
-    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;
+        super(file, 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);
-        }
+    protected int maxNameLength() {
+        return 255;
     }
 
-    @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();
-        }
-    }
 }
diff --git a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
index 48df9b7..2001456 100644
--- a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
+++ b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
@@ -36,16 +36,6 @@
 
 #include "sun_nio_fs_LinuxNativeDispatcher.h"
 
-typedef size_t fgetxattr_func(int fd, const char* name, void* value, size_t size);
-typedef int fsetxattr_func(int fd, const char* name, void* value, size_t size, int flags);
-typedef int fremovexattr_func(int fd, const char* name);
-typedef int flistxattr_func(int fd, char* list, size_t size);
-
-fgetxattr_func* my_fgetxattr_func = NULL;
-fsetxattr_func* my_fsetxattr_func = NULL;
-fremovexattr_func* my_fremovexattr_func = NULL;
-flistxattr_func* my_flistxattr_func = NULL;
-
 static jfieldID entry_name;
 static jfieldID entry_dir;
 static jfieldID entry_fstype;
@@ -62,11 +52,6 @@
 JNIEXPORT void JNICALL
 Java_sun_nio_fs_LinuxNativeDispatcher_init(JNIEnv *env, jclass clazz)
 {
-    my_fgetxattr_func = (fgetxattr_func*)dlsym(RTLD_DEFAULT, "fgetxattr");
-    my_fsetxattr_func = (fsetxattr_func*)dlsym(RTLD_DEFAULT, "fsetxattr");
-    my_fremovexattr_func = (fremovexattr_func*)dlsym(RTLD_DEFAULT, "fremovexattr");
-    my_flistxattr_func = (flistxattr_func*)dlsym(RTLD_DEFAULT, "flistxattr");
-
     clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry");
     CHECK_NULL(clazz);
     entry_name = (*env)->GetFieldID(env, clazz, "name", "[B");
@@ -79,78 +64,6 @@
     CHECK_NULL(entry_options);
 }
 
-JNIEXPORT jint JNICALL
-Java_sun_nio_fs_LinuxNativeDispatcher_fgetxattr0(JNIEnv* env, jclass clazz,
-    jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
-{
-    size_t res = -1;
-    const char* name = jlong_to_ptr(nameAddress);
-    void* value = jlong_to_ptr(valueAddress);
-
-    if (my_fgetxattr_func == NULL) {
-        errno = ENOTSUP;
-    } else {
-        /* EINTR not documented */
-        res = (*my_fgetxattr_func)(fd, name, value, valueLen);
-    }
-    if (res == (size_t)-1)
-        throwUnixException(env, errno);
-    return (jint)res;
-}
-
-JNIEXPORT void JNICALL
-Java_sun_nio_fs_LinuxNativeDispatcher_fsetxattr0(JNIEnv* env, jclass clazz,
-    jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
-{
-    int res = -1;
-    const char* name = jlong_to_ptr(nameAddress);
-    void* value = jlong_to_ptr(valueAddress);
-
-    if (my_fsetxattr_func == NULL) {
-        errno = ENOTSUP;
-    } else {
-        /* EINTR not documented */
-        res = (*my_fsetxattr_func)(fd, name, value, valueLen, 0);
-    }
-    if (res == -1)
-        throwUnixException(env, errno);
-}
-
-JNIEXPORT void JNICALL
-Java_sun_nio_fs_LinuxNativeDispatcher_fremovexattr0(JNIEnv* env, jclass clazz,
-    jint fd, jlong nameAddress)
-{
-    int res = -1;
-    const char* name = jlong_to_ptr(nameAddress);
-
-    if (my_fremovexattr_func == NULL) {
-        errno = ENOTSUP;
-    } else {
-        /* EINTR not documented */
-        res = (*my_fremovexattr_func)(fd, name);
-    }
-    if (res == -1)
-        throwUnixException(env, errno);
-}
-
-JNIEXPORT jint JNICALL
-Java_sun_nio_fs_LinuxNativeDispatcher_flistxattr(JNIEnv* env, jclass clazz,
-    jint fd, jlong listAddress, jint size)
-{
-    size_t res = -1;
-    char* list = jlong_to_ptr(listAddress);
-
-    if (my_flistxattr_func == NULL) {
-        errno = ENOTSUP;
-    } else {
-        /* EINTR not documented */
-        res = (*my_flistxattr_func)(fd, list, (size_t)size);
-    }
-    if (res == (size_t)-1)
-        throwUnixException(env, errno);
-    return (jint)res;
-}
-
 JNIEXPORT jlong JNICALL
 Java_sun_nio_fs_LinuxNativeDispatcher_setmntent0(JNIEnv* env, jclass this, jlong pathAddress,
                                                  jlong modeAddress)
diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java b/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java
index 8083ebc..3f5de6b 100644
--- a/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java
+++ b/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java
@@ -80,27 +80,6 @@
         throw new IOException("Mount point not found in fstab");
     }
 
-    // returns true if extended attributes enabled on file system where given
-    // file resides, returns false if disabled or unable to determine.
-    private boolean isExtendedAttributesEnabled(UnixPath path) {
-        int fd = -1;
-        try {
-            fd = path.openForAttributeAccess(false);
-
-            // fgetxattr returns size if called with size==0
-            byte[] name = Util.toBytes("user.java");
-            BsdNativeDispatcher.fgetxattr(fd, name, 0L, 0);
-            return true;
-        } catch (UnixException e) {
-            // attribute does not exist
-            if (e.errno() == UnixConstants.ENOATTR)
-                return true;
-        } finally {
-            UnixNativeDispatcher.close(fd);
-        }
-        return false;
-    }
-
     @Override
     public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
         // support UserDefinedAttributeView if extended attributes enabled
diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java b/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
index 3f6b993..359da72 100644
--- a/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
+++ b/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
@@ -69,6 +69,7 @@
 
     @Override
     void copyNonPosixAttributes(int ofd, int nfd) {
+        UnixUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
     }
 
     /**
diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java b/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java
index fecc58e..2e05313 100644
--- a/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java
+++ b/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java
@@ -62,67 +62,6 @@
     }
     static native byte[] getmntonname0(long pathAddress) throws UnixException;
 
-    /**
-     * ssize_t fgetxattr(int fd, const char *name, void *value, size_t size,
-     *  u_int32_t position, int options);
-     */
-    static int fgetxattr(int fd, byte[] name, long valueAddress,
-                         int valueLen) throws UnixException
-    {
-        NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
-        try {
-            return fgetxattr0(fd, buffer.address(), valueAddress, valueLen, 0L, 0);
-        } finally {
-            buffer.release();
-        }
-    }
-
-    private static native int fgetxattr0(int fd, long nameAddress,
-        long valueAddress, int valueLen, long position, int options) throws UnixException;
-
-    /**
-     * int fsetxattr(int fd, const char *name, void *value, size_t size,
-     *  u_int32_t position, int options);
-     */
-    static void fsetxattr(int fd, byte[] name, long valueAddress,
-                          int valueLen) throws UnixException
-    {
-        NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
-        try {
-            fsetxattr0(fd, buffer.address(), valueAddress, valueLen, 0L, 0);
-        } finally {
-            buffer.release();
-        }
-    }
-
-    private static native void fsetxattr0(int fd, long nameAddress,
-        long valueAddress, int valueLen, long position, int options) throws UnixException;
-
-    /**
-     * int fremovexattr(int fd, const char *name, int options);
-     */
-    static void fremovexattr(int fd, byte[] name) throws UnixException {
-        NativeBuffer buffer = NativeBuffers.asNativeBuffer(name);
-        try {
-            fremovexattr0(fd, buffer.address(), 0);
-        } finally {
-            buffer.release();
-        }
-    }
-
-    private static native void fremovexattr0(int fd, long nameAddress, int options)
-        throws UnixException;
-
-    /**
-     * ssize_t flistxattr(int fd, char *namebuf, size_t size, int options);
-     */
-    static int flistxattr(int fd, long nameBufAddress, int size) throws UnixException {
-        return flistxattr0(fd, nameBufAddress, size, 0);
-    }
-
-    private static native int flistxattr0(int fd, long nameBufAddress, int size,
-        int options) throws UnixException;
-
     // initialize field IDs
     private static native void initIDs();
 
diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdUserDefinedFileAttributeView.java b/src/java.base/macosx/classes/sun/nio/fs/BsdUserDefinedFileAttributeView.java
index 5edc972..725383e 100644
--- a/src/java.base/macosx/classes/sun/nio/fs/BsdUserDefinedFileAttributeView.java
+++ b/src/java.base/macosx/classes/sun/nio/fs/BsdUserDefinedFileAttributeView.java
@@ -25,352 +25,18 @@
 
 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.BsdNativeDispatcher.*;
-
-/**
- * BSD implementation of UserDefinedFileAttributeView using extended attributes.
- */
-
 class BsdUserDefinedFileAttributeView
-    extends AbstractUserDefinedFileAttributeView
+    extends UnixUserDefinedFileAttributeView
 {
-    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),
-    // see XATTR_MAXNAMELEN in https://github.com/apple/darwin-xnu/blob/master/bsd/sys/xattr.h
-    private static final int XATTR_NAME_MAX = 127;
-
-    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;
 
     BsdUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
-        this.file = file;
-        this.followLinks = followLinks;
+        super(file, 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);
-        }
+    protected int maxNameLength() {
+        // see XATTR_MAXNAMELEN in https://github.com/apple/darwin-xnu/blob/master/bsd/sys/xattr.h
+        return 127;
     }
 
-    @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();
-        }
-    }
 }
diff --git a/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c b/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c
index e8296d4e..056d08e 100644
--- a/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c
+++ b/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c
@@ -30,7 +30,6 @@
 
 #include <sys/param.h>
 #include <sys/mount.h>
-#include <sys/xattr.h>
 #ifdef ST_RDONLY
 #define statfs statvfs
 #define getfsstat getvfsstat
@@ -225,52 +224,3 @@
 
     return mntonname;
 }
-
-JNIEXPORT jint JNICALL
-Java_sun_nio_fs_BsdNativeDispatcher_fgetxattr0(JNIEnv* env, jclass clazz,
-    jint fd, jlong nameAddress, jlong valueAddress, jint valueLen, jlong position, jint options)
-{
-    const char* name = jlong_to_ptr(nameAddress);
-    void* value = jlong_to_ptr(valueAddress);
-
-    ssize_t res = fgetxattr(fd, name, value, valueLen, (u_int32_t)position, options);
-    if (res == (ssize_t)-1)
-        throwUnixException(env, errno);
-    return (jint)res;
-}
-
-JNIEXPORT void JNICALL
-Java_sun_nio_fs_BsdNativeDispatcher_fsetxattr0(JNIEnv* env, jclass clazz,
-    jint fd, jlong nameAddress, jlong valueAddress, jint valueLen, jlong position, jint options)
-{
-    const char* name = jlong_to_ptr(nameAddress);
-    void* value = jlong_to_ptr(valueAddress);
-
-    int res = fsetxattr(fd, name, value, valueLen, (u_int32_t)position, options);
-    if (res == -1)
-        throwUnixException(env, errno);
-}
-
-JNIEXPORT void JNICALL
-Java_sun_nio_fs_BsdNativeDispatcher_fremovexattr0(JNIEnv* env, jclass clazz,
-    jint fd, jlong nameAddress, jint options)
-{
-    const char* name = jlong_to_ptr(nameAddress);
-
-    int res = fremovexattr(fd, name, options);
-    if (res == -1)
-        throwUnixException(env, errno);
-}
-
-JNIEXPORT jint JNICALL
-Java_sun_nio_fs_BsdNativeDispatcher_flistxattr0(JNIEnv* env, jclass clazz,
-    jint fd, jlong nameBufAddress, jint size, jint options)
-{
-    char* nameBuf = jlong_to_ptr(nameBufAddress);
-
-    ssize_t res = flistxattr(fd, nameBuf, (size_t)size, options);
-
-    if (res == (ssize_t)-1)
-        throwUnixException(env, errno);
-    return (jint)res;
-}
diff --git a/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java b/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java
index 6794f13..9500a73 100644
--- a/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java
+++ b/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java
@@ -33,7 +33,7 @@
  * A light-weight buffer in native memory.
  */
 
-class NativeBuffer {
+class NativeBuffer implements AutoCloseable {
     private static final Unsafe unsafe = Unsafe.getUnsafe();
 
     private final long address;
@@ -61,6 +61,11 @@
                                        .register(this, new Deallocator(address));
     }
 
+    @Override
+    public void close() {
+        release();
+    }
+
     void release() {
         NativeBuffers.releaseNativeBuffer(this);
     }
diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template
index a9018f8..d60283d 100644
--- a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template
@@ -117,9 +117,14 @@
     static final int PREFIX_ENODATA = ENODATA;
 #endif
 
-#ifdef ENOATTR
-    // BSD uses ENOATTR instead of ENODATA during xattr calls
-    static final int PREFIX_ENOATTR = ENOATTR;
+// fgetxattr error codes for absent attributes depend on the OS:
+#ifdef _ALLBSD_SOURCE
+    static final int PREFIX_XATTR_NOT_FOUND = ENOATTR;
+#elif __linux__
+    static final int PREFIX_XATTR_NOT_FOUND = ENODATA;
+#else
+    // not supported (dummy values will not be used at runtime).
+    static final int PREFIX_XATTR_NOT_FOUND = 00;
 #endif
 
     static final int PREFIX_ERANGE = ERANGE;
diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixFileStore.java b/src/java.base/unix/classes/sun/nio/fs/UnixFileStore.java
index 3ff72fa..45d6114 100644
--- a/src/java.base/unix/classes/sun/nio/fs/UnixFileStore.java
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixFileStore.java
@@ -172,6 +172,31 @@
         throw new UnsupportedOperationException("'" + attribute + "' not recognized");
     }
 
+    /**
+     * Checks whether extended attributes are enabled on the file system where the given file resides.
+     *
+     * @param path A path pointing to an existing node, such as the file system's root
+     * @return <code>true</code> if enabled, <code>false</code> if disabled or unable to determine
+     */
+    protected boolean isExtendedAttributesEnabled(UnixPath path) {
+        int fd = -1;
+        try {
+            fd = path.openForAttributeAccess(false);
+
+            // fgetxattr returns size if called with size==0
+            byte[] name = Util.toBytes("user.java");
+            UnixNativeDispatcher.fgetxattr(fd, name, 0L, 0);
+            return true;
+        } catch (UnixException e) {
+            // attribute does not exist
+            if (e.errno() == UnixConstants.XATTR_NOT_FOUND)
+                return true;
+        } finally {
+            UnixNativeDispatcher.close(fd);
+        }
+        return false;
+    }
+
     @Override
     public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
         if (type == null)
diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java
index a9dc76f..1b0c4ef 100644
--- a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java
@@ -564,6 +564,52 @@
     static native byte[] strerror(int errnum);
 
     /**
+     * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
+     */
+    static int fgetxattr(int filedes, byte[] name, long valueAddress,
+                         int valueLen) throws UnixException
+    {
+        try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(name)) {
+            return fgetxattr0(filedes, buffer.address(), valueAddress, valueLen);
+        }
+    }
+
+    private static native int fgetxattr0(int filedes, long nameAddress,
+        long valueAddress, int valueLen) throws UnixException;
+
+    /**
+     *  fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags);
+     */
+    static void fsetxattr(int filedes, byte[] name, long valueAddress,
+                          int valueLen) throws UnixException
+    {
+        try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(name)) {
+            fsetxattr0(filedes, buffer.address(), valueAddress, valueLen);
+        }
+    }
+
+    private static native void fsetxattr0(int filedes, long nameAddress,
+        long valueAddress, int valueLen) throws UnixException;
+
+    /**
+     * fremovexattr(int filedes, const char *name);
+     */
+    static void fremovexattr(int filedes, byte[] name) throws UnixException {
+        try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(name)) {
+            fremovexattr0(filedes, buffer.address());
+        }
+    }
+
+    private static native void fremovexattr0(int filedes, long nameAddress)
+        throws UnixException;
+
+    /**
+     * size_t flistxattr(int filedes, const char *list, size_t size)
+     */
+    static native int flistxattr(int filedes, long listAddress, int size)
+        throws UnixException;
+
+    /**
      * Capabilities
      */
     private static final int SUPPORTS_OPENAT        = 1 << 1;  // syscalls
diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixUserDefinedFileAttributeView.java b/src/java.base/unix/classes/sun/nio/fs/UnixUserDefinedFileAttributeView.java
new file mode 100644
index 0000000..bfe08c8
--- /dev/null
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixUserDefinedFileAttributeView.java
@@ -0,0 +1,375 @@
+/*
+ * 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.UnixNativeDispatcher.*;
+
+/**
+ * Unix implementation of UserDefinedFileAttributeView using extended attributes.
+ */
+abstract class UnixUserDefinedFileAttributeView
+    extends AbstractUserDefinedFileAttributeView
+{
+    private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+    // namespace for extended user attributes
+    private static final String USER_NAMESPACE = "user.";
+
+    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 > maxNameLength()) {
+            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;
+
+    UnixUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
+        this.file = file;
+        this.followLinks = followLinks;
+    }
+
+    /**
+     * @return the maximum supported length of xattr names (in bytes, including namespace)
+     */
+    protected abstract int maxNameLength();
+
+    @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();
+        }
+    }
+
+    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();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
index 74ccaac..fe74284 100644
--- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
+++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
@@ -41,6 +41,10 @@
 #endif
 #include <sys/time.h>
 
+#if defined(__linux__) || defined(_ALLBSD_SOURCE)
+#include <sys/xattr.h>
+#endif
+
 /* For POSIX-compliant getpwuid_r */
 #include <pwd.h>
 #include <grp.h>
@@ -1241,3 +1245,83 @@
 
     return gid;
 }
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fgetxattr0(JNIEnv* env, jclass clazz,
+    jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
+{
+    size_t res = -1;
+    const char* name = jlong_to_ptr(nameAddress);
+    void* value = jlong_to_ptr(valueAddress);
+
+#ifdef __linux__
+    res = fgetxattr(fd, name, value, valueLen);
+#elif _ALLBSD_SOURCE
+    res = fgetxattr(fd, name, value, valueLen, 0, 0);
+#else
+    throwUnixException(env, ENOTSUP);
+#endif
+
+    if (res == (size_t)-1)
+        throwUnixException(env, errno);
+    return (jint)res;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fsetxattr0(JNIEnv* env, jclass clazz,
+    jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
+{
+    int res = -1;
+    const char* name = jlong_to_ptr(nameAddress);
+    void* value = jlong_to_ptr(valueAddress);
+
+#ifdef __linux__
+    res = fsetxattr(fd, name, value, valueLen, 0);
+#elif _ALLBSD_SOURCE
+    res = fsetxattr(fd, name, value, valueLen, 0, 0);
+#else
+    throwUnixException(env, ENOTSUP);
+#endif
+
+    if (res == -1)
+        throwUnixException(env, errno);
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_fremovexattr0(JNIEnv* env, jclass clazz,
+    jint fd, jlong nameAddress)
+{
+    int res = -1;
+    const char* name = jlong_to_ptr(nameAddress);
+
+#ifdef __linux__
+    res = fremovexattr(fd, name);
+#elif _ALLBSD_SOURCE
+    res = fremovexattr(fd, name, 0);
+#else
+    throwUnixException(env, ENOTSUP);
+#endif
+
+    if (res == -1)
+        throwUnixException(env, errno);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_fs_UnixNativeDispatcher_flistxattr(JNIEnv* env, jclass clazz,
+    jint fd, jlong listAddress, jint size)
+{
+    size_t res = -1;
+    char* list = jlong_to_ptr(listAddress);
+
+#ifdef __linux__
+    res = flistxattr(fd, list, (size_t)size);
+#elif _ALLBSD_SOURCE
+    res = flistxattr(fd, list, (size_t)size, 0);
+#else
+    throwUnixException(env, ENOTSUP);
+#endif
+
+    if (res == (size_t)-1)
+        throwUnixException(env, errno);
+    return (jint)res;
+}
\ No newline at end of file