Pre-allocating drive space for file storage

后端 未结 4 1040
挽巷
挽巷 2021-02-06 11:39

Is there a Java way to pre-allocate drive space for exclusive usage in the application?

There is no requirement for this space to be a separate filesystem or a part of

4条回答
  •  闹比i
    闹比i (楼主)
    2021-02-06 12:34

    Here's a stripped down version of my JNA-based fallocate solution. The main trick is obtaining the native file descriptor. I've only tested it on Linux so far, but it should work on all modern POSIX/non-Windows systems. It's not necessary on Windows, as Windows does not create sparse files by default (only with StandardOpenOption.SPARSE), so RandomAccessFile.setLength(size) or FileChannel.write(ByteBuffer.allocate(1), size - 1) are adequate there.

    /**
     * Provides access to operating system-specific {@code fallocate} and
     * {@code posix_fallocate} functions.
     */
    public final class Fallocate {
    
        private static final boolean IS_LINUX = Platform.isLinux();
        private static final boolean IS_POSIX = !Platform.isWindows();
    
        private static final int FALLOC_FL_KEEP_SIZE = 0x01;
    
        private final int fd;
        private int mode;
        private long offset;
        private final long length;
    
        private Fallocate(int fd, long length) {
            if (!isSupported()) {
                throwUnsupported("fallocate");
            }
            this.fd = fd;
            this.length = length;
        }
    
        public static boolean isSupported() {
            return IS_POSIX;
        }
    
        public static Fallocate forChannel(FileChannel channel, long length) {
            return new Fallocate(getDescriptor(channel), length);
        }
    
        public static Fallocate forDescriptor(FileDescriptor descriptor, long length) {
            return new Fallocate(getDescriptor(descriptor), length);
        }
    
        public Fallocate fromOffset(long offset) {
            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 operating system");
        }
    
        public void execute() throws IOException {
            final int errno;
            if (IS_LINUX) {
                final int result = FallocateHolder.fallocate(fd, mode, offset, length);
                errno = result == 0 ? 0 : Native.getLastError();
            } else {
                errno = PosixFallocateHolder.posix_fallocate(fd, offset, length);
            }
            if (errno != 0) {
                throw new IOException("fallocate returned " + errno);
            }
        }
    
        private static class FallocateHolder {
    
            static {
                Native.register(Platform.C_LIBRARY_NAME);
            }
    
            private static native int fallocate(int fd, int mode, long offset, long length);
        }
    
        private static class PosixFallocateHolder {
    
            static {
                Native.register(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("fd");
                field.setAccessible(true);
                return (int) field.get(descriptor);
            } catch (final Exception e) {
                throw new UnsupportedOperationException("unsupported FileDescriptor implementation", e);
            }
        }
    }
    

提交回复
热议问题