Skip to content
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 #1010

Open
wants to merge 3 commits into
base: next
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 39 additions & 29 deletions src/freenet/support/io/Fallocate.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.nio.channels.FileChannel;

import freenet.support.Logger;
import freenet.support.math.MersenneTwister;

/**
* Provides access to operating system-specific {@code fallocate} and
Expand All @@ -20,6 +19,7 @@
public final class Fallocate {

private static final boolean IS_LINUX = Platform.isLinux();
private static final boolean IS_WINDOWS = Platform.isWindows();
private static final boolean IS_POSIX = !Platform.isWindows() && !Platform.isMac() && !Platform.isOpenBSD();
private static final boolean IS_ANDROID = Platform.isAndroid();

Expand All @@ -45,42 +45,49 @@ public static Fallocate forChannel(FileChannel channel, FileDescriptor fd, long
return new Fallocate(channel, getDescriptor(fd), final_filesize);
}

/**
* @throws IllegalArgumentException
*/
public Fallocate fromOffset(long offset) {
if(offset < 0 || offset > final_filesize) throw new IllegalArgumentException();
this.offset = offset;
return this;
}

/**
* This method only works for Linux, do not use it.
* @throws UnsupportedOperationException
*/
@Deprecated
public Fallocate keepSize() {
requireLinux("fallocate keep size");
if (!IS_LINUX) {
throw new UnsupportedOperationException("fallocate keep size is not supported on this file system");
}
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);
if (fd > 2) {
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 {
isUnsupported = true;
}

if (isUnsupported || errno != 0) {
Logger.normal(this, "fallocate() failed; using legacy method; errno="+errno);
if (errno != 0) {
// OS supports fallocate() but it failed. Do not log if the OS does not support fallocate().
Logger.normal(this, "fallocate() failed; using legacy method; errno=" + errno);
}
legacyFill(channel, final_filesize, offset);
}
}
Expand Down Expand Up @@ -108,7 +115,8 @@ private static int getDescriptor(FileChannel channel) {
field.setAccessible(true);
return getDescriptor((FileDescriptor) field.get(channel));
} catch (final Exception e) {
throw new UnsupportedOperationException("unsupported FileChannel implementation", e);
// File descriptor is not supported: fd is null and unavailable, return 0
return 0;
}
}

Expand All @@ -119,20 +127,22 @@ private static int getDescriptor(FileDescriptor descriptor) {
field.setAccessible(true);
return (int) field.get(descriptor);
} catch (final Exception e) {
throw new UnsupportedOperationException("unsupported FileDescriptor implementation", e);
// File descriptor is not supported: fd is null and unavailable, return 0
return 0;
}
}

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();
if (IS_WINDOWS) {
// Windows do not create sparse files by default, so just write a byte at the end.
fc.write(ByteBuffer.allocate(1), newLength - 1);
} else {
// fill fc with zeros
byte[] b = new byte[4096];
ByteBuffer bb = ByteBuffer.wrap(b);
while (offset < newLength) {
bb.rewind();
offset += fc.write(bb, offset);
}
}
}
Expand Down