-
Notifications
You must be signed in to change notification settings - Fork 213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix unhandled UnsupportedOperationException in Fallocate #1008
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,121 +19,138 @@ | |
*/ | ||
public final class Fallocate { | ||
|
||
private static final boolean IS_LINUX = Platform.isLinux(); | ||
private static final boolean IS_POSIX = !Platform.isWindows() && !Platform.isMac() && !Platform.isOpenBSD(); | ||
private static final boolean IS_ANDROID = Platform.isAndroid(); | ||
|
||
private static final int FALLOC_FL_KEEP_SIZE = 0x01; | ||
|
||
private final int fd; | ||
private int mode; | ||
private long offset; | ||
private final long final_filesize; | ||
private final FileChannel channel; | ||
|
||
private Fallocate(FileChannel channel, int fd, long final_filesize) { | ||
this.fd = fd; | ||
this.final_filesize = final_filesize; | ||
this.channel = channel; | ||
} | ||
|
||
public static Fallocate forChannel(FileChannel channel, long final_filesize) { | ||
return new Fallocate(channel, getDescriptor(channel), final_filesize); | ||
} | ||
|
||
public static Fallocate forChannel(FileChannel channel, FileDescriptor fd, long final_filesize) { | ||
return new Fallocate(channel, getDescriptor(fd), final_filesize); | ||
} | ||
|
||
public Fallocate fromOffset(long offset) { | ||
if(offset < 0 || offset > final_filesize) throw new IllegalArgumentException(); | ||
this.offset = offset; | ||
return this; | ||
} | ||
|
||
public Fallocate keepSize() { | ||
requireLinux("fallocate keep size"); | ||
mode |= FALLOC_FL_KEEP_SIZE; | ||
return this; | ||
} | ||
|
||
private void requireLinux(String feature) { | ||
if (!IS_LINUX) { | ||
throwUnsupported(feature); | ||
} | ||
} | ||
|
||
private void throwUnsupported(String feature) { | ||
throw new UnsupportedOperationException(feature + " is not supported on this file system"); | ||
} | ||
|
||
public void execute() throws IOException { | ||
int errno = 0; | ||
boolean isUnsupported = false; | ||
if (IS_LINUX) { | ||
final int result = FallocateHolder.fallocate(fd, mode, offset, final_filesize-offset); | ||
errno = result == 0 ? 0 : Native.getLastError(); | ||
} else if (IS_POSIX) { | ||
errno = FallocateHolderPOSIX.posix_fallocate(fd, offset, final_filesize-offset); | ||
} else { | ||
isUnsupported = true; | ||
} | ||
|
||
if (isUnsupported || errno != 0) { | ||
Logger.normal(this, "fallocate() failed; using legacy method; errno="+errno); | ||
legacyFill(channel, final_filesize, offset); | ||
} | ||
} | ||
|
||
private static class FallocateHolder { | ||
static { | ||
Native.register(FallocateHolder.class, Platform.C_LIBRARY_NAME); | ||
} | ||
|
||
private static native int fallocate(int fd, int mode, long offset, long length); | ||
} | ||
|
||
private static class FallocateHolderPOSIX { | ||
static { | ||
Native.register(FallocateHolderPOSIX.class, Platform.C_LIBRARY_NAME); | ||
} | ||
|
||
private static native int posix_fallocate(int fd, long offset, long length); | ||
} | ||
|
||
private static int getDescriptor(FileChannel channel) { | ||
try { | ||
// sun.nio.ch.FileChannelImpl declares private final java.io.FileDescriptor fd | ||
final Field field = channel.getClass().getDeclaredField("fd"); | ||
field.setAccessible(true); | ||
return getDescriptor((FileDescriptor) field.get(channel)); | ||
} catch (final Exception e) { | ||
throw new UnsupportedOperationException("unsupported FileChannel implementation", e); | ||
} | ||
} | ||
|
||
private static int getDescriptor(FileDescriptor descriptor) { | ||
try { | ||
// Oracle java.io.FileDescriptor declares private int fd | ||
final Field field = descriptor.getClass().getDeclaredField(IS_ANDROID ? "descriptor" : "fd"); | ||
field.setAccessible(true); | ||
return (int) field.get(descriptor); | ||
} catch (final Exception e) { | ||
throw new UnsupportedOperationException("unsupported FileDescriptor implementation", e); | ||
} | ||
} | ||
|
||
private static void legacyFill(FileChannel fc, long newLength, long offset) throws IOException { | ||
MersenneTwister mt = new MersenneTwister(); | ||
byte[] b = new byte[4096]; | ||
ByteBuffer bb = ByteBuffer.wrap(b); | ||
while (offset < newLength) { | ||
bb.rewind(); | ||
mt.nextBytes(b); | ||
offset += fc.write(bb, offset); | ||
if (offset % (1024 * 1024 * 1024L) == 0) { | ||
mt = new MersenneTwister(); | ||
} | ||
} | ||
} | ||
private static final boolean IS_LINUX = Platform.isLinux(); | ||
private static final boolean IS_POSIX = !Platform.isWindows() && !Platform.isMac() && !Platform.isOpenBSD(); | ||
private static final boolean IS_ANDROID = Platform.isAndroid(); | ||
|
||
private static final int FALLOC_FL_KEEP_SIZE = 0x01; | ||
|
||
private final int fd; | ||
private int mode; | ||
private long offset; | ||
private final long final_filesize; | ||
private final FileChannel channel; | ||
|
||
private Fallocate(FileChannel channel, int fd, long final_filesize) { | ||
this.fd = fd; | ||
this.final_filesize = final_filesize; | ||
this.channel = channel; | ||
} | ||
|
||
public static Fallocate forChannel(FileChannel channel, long final_filesize) { | ||
try { | ||
return new Fallocate(channel, getDescriptor(channel), final_filesize); | ||
} catch (final UnsupportedOperationException exception) { | ||
return new Fallocate(channel, 0, final_filesize); | ||
} | ||
} | ||
|
||
public static Fallocate forChannel(FileChannel channel, FileDescriptor fd, long final_filesize) { | ||
try { | ||
return new Fallocate(channel, getDescriptor(fd), final_filesize); | ||
} catch (final UnsupportedOperationException exception) { | ||
return new Fallocate(channel, 0, final_filesize); | ||
} | ||
} | ||
|
||
public Fallocate fromOffset(long offset) throws IllegalArgumentException { | ||
if(offset < 0 || offset > final_filesize) throw new IllegalArgumentException(); | ||
this.offset = offset; | ||
return this; | ||
} | ||
|
||
public Fallocate keepSize() throws UnsupportedOperationException { | ||
requireLinux("fallocate keep size"); | ||
mode |= FALLOC_FL_KEEP_SIZE; | ||
return this; | ||
} | ||
|
||
private void requireLinux(String feature) throws UnsupportedOperationException { | ||
if (!IS_LINUX) { | ||
throwUnsupported(feature); | ||
} | ||
} | ||
|
||
private void throwUnsupported(String feature) throws UnsupportedOperationException { | ||
throw new UnsupportedOperationException(feature + " is not supported on this file system"); | ||
} | ||
|
||
public void execute() throws IOException { | ||
int errno = 0; | ||
boolean isUnsupported = false; | ||
if (fd != 0) { | ||
if (IS_LINUX) { | ||
final int result = FallocateHolder.fallocate(fd, mode, offset, final_filesize-offset); | ||
errno = result == 0 ? 0 : Native.getLastError(); | ||
} else if (IS_POSIX) { | ||
errno = FallocateHolderPOSIX.posix_fallocate(fd, offset, final_filesize-offset); | ||
} else { | ||
isUnsupported = true; | ||
} | ||
} else { | ||
if (Platform.isWindows()) { | ||
// On Windows, just write a byte at the end. | ||
channel.write(ByteBuffer.allocate(1), final_filesize - 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this safe? |
||
} else { | ||
isUnsupported = true; | ||
} | ||
} | ||
|
||
if (isUnsupported || errno != 0) { | ||
Logger.normal(this, "fallocate() failed; using legacy method; errno=" + errno); | ||
legacyFill(channel, final_filesize, offset); | ||
} | ||
} | ||
|
||
private static class FallocateHolder { | ||
static { | ||
Native.register(FallocateHolder.class, Platform.C_LIBRARY_NAME); | ||
} | ||
|
||
private static native int fallocate(int fd, int mode, long offset, long length); | ||
} | ||
|
||
private static class FallocateHolderPOSIX { | ||
static { | ||
Native.register(FallocateHolderPOSIX.class, Platform.C_LIBRARY_NAME); | ||
} | ||
|
||
private static native int posix_fallocate(int fd, long offset, long length); | ||
} | ||
|
||
private static int getDescriptor(FileChannel channel) throws UnsupportedOperationException { | ||
try { | ||
// sun.nio.ch.FileChannelImpl declares private final java.io.FileDescriptor fd | ||
final Field field = channel.getClass().getDeclaredField("fd"); | ||
field.setAccessible(true); | ||
return getDescriptor((FileDescriptor) field.get(channel)); | ||
} catch (final Exception e) { | ||
throw new UnsupportedOperationException("unsupported FileChannel implementation", e); | ||
} | ||
} | ||
|
||
private static int getDescriptor(FileDescriptor descriptor) throws UnsupportedOperationException { | ||
try { | ||
// Oracle java.io.FileDescriptor declares private int fd | ||
final Field field = descriptor.getClass().getDeclaredField(IS_ANDROID ? "descriptor" : "fd"); | ||
field.setAccessible(true); | ||
return (int) field.get(descriptor); | ||
} catch (final Exception e) { | ||
throw new UnsupportedOperationException("unsupported FileDescriptor implementation", e); | ||
} | ||
} | ||
|
||
private static void legacyFill(FileChannel fc, long newLength, long offset) throws IOException { | ||
MersenneTwister mt = new MersenneTwister(); | ||
byte[] b = new byte[4096]; | ||
ByteBuffer bb = ByteBuffer.wrap(b); | ||
while (offset < newLength) { | ||
bb.rewind(); | ||
mt.nextBytes(b); | ||
offset += fc.write(bb, offset); | ||
if (offset % (1024 * 1024 * 1024L) == 0) { | ||
mt = new MersenneTwister(); | ||
} | ||
} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to have a comment here that explains why 0 is the right file descriptor.