8260966: (fs) Consolidate Linux and macOS implementations of UserDefinedFileAttributeView

8260691: (fs) LinuxNativeDispatcher should link to xattr functions

Reviewed-by: alanb
This commit is contained in:
Sebastian Stenzel 2021-03-02 14:40:45 +00:00 committed by Alan Bateman
parent 5f4bc0aca6
commit 0de6abd4b4
16 changed files with 555 additions and 977 deletions

View file

@ -105,27 +105,6 @@ class LinuxFileStore
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+");

View file

@ -69,7 +69,7 @@ class LinuxFileSystem extends UnixFileSystem {
@Override
void copyNonPosixAttributes(int ofd, int nfd) {
LinuxUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
UnixUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
}
/**

View file

@ -69,61 +69,6 @@ class LinuxNativeDispatcher extends UnixNativeDispatcher {
*/
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();

View file

@ -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();
}
}
}

View file

@ -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 @@ static void throwUnixException(JNIEnv* env, int errnum) {
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 @@ Java_sun_nio_fs_LinuxNativeDispatcher_init(JNIEnv *env, jclass clazz)
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)