java nio的使用,使用Java NIO直接访问Windows磁盘

I am using a library that uses Java NIO in order to directly map files to memory, but I am having trouble reading disks directly.

I can read the disks directly using FileInputStream with UNC, such as

File disk = new File("\\\\.\\PhysicalDrive0\\");

try (FileInputStream fis = new FileInputStream(disk);

BufferedInputStream bis = new BufferedInputStream(fis)) {

byte[] somebytes = new byte[10];

bis.read(somebytes);

} catch (Exception ex) {

System.out.println("Oh bother");

}

However, I can't extend this to NIO:

File disk = new File("\\\\.\\PhysicalDrive0\\");

Path path = disk.toPath();

try (FileChannel fc = FileChannel.open(path, StandardOpenOption.READ)){

System.out.println("No exceptions! Yay!");

} catch (Exception ex) {

System.out.println("Oh bother");

}

The stacktrace (up to the cause) is:

java.nio.file.FileSystemException: \\.\PhysicalDrive0\: The parameter is incorrect.

at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)

at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)

at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)

at sun.nio.fs.WindowsFileSystemProvider.newFileChannel(WindowsFileSystemProvider.java:115)

at java.nio.channels.FileChannel.open(FileChannel.java:287)

at java.nio.channels.FileChannel.open(FileChannel.java:334)

at hdreader.HDReader.testcode(HDReader.java:147)

I haven't been able to find a solution, though I saw something close on How to access specific raw data on disk from java. The answer by Daniel Alder suggesting the use of GLOBALROOT seems to be relevant, as the answer uses FileChannel in the answer, but I can't seem to find the drive using this pattern. Is there a way to list all devices under GLOBALROOT or something like that?

At the moment I am looking at replacing uses of NIO with straight InputStreams, but I want to avoid this if I can. Firstly, NIO was used for a reason, and secondly, it runs through a lot of code and will require a lot of work. Finally, I'd like to know how to implement something like Daniel's solution so that I can write to devices or use NIO in the future.

So in summary: how can I access drives directly with Java NIO (not InputStreams), and/or is there a way to list all devices accessible through GLOBALROOT so that I might use Daniel Alser's solution?

Summary of Answers:

I have kept the past edits (below) to avoid confusion. With the help of EJP and Apangin I think I have a workable solution. Something like

private void rafMethod(long posn) {

ByteBuffer buffer = ByteBuffer.allocate(512);

buffer.rewind();

try (RandomAccessFile raf = new RandomAccessFile(disk.getPath(), "r");

SeekableByteChannel sbc = raf.getChannel()) {

sbc.read(buffer);

} catch (Exception ex) {

System.out.println("Oh bother: " + ex);

ex.printStackTrace();

}

return buffer;

}

This will work as long as the posn parameter is a multiple of the sector size (set at 512 in this case). Note that this also works with the Channels.newChannel(FileInputStream), which seems to always return a SeekableByteStream in this case and it appears it is safe to cast it to one.

From quick and dirty testing it appears that these methods truly do seek and don't just skip. I searched for a thousand locations at the start of my drive and it read them. I did the same but added an offset of half of the disk size (to search the back of the disk). I found:

Both methods took almost the same time.

Searching the start or the end of the disk did not affect time.

Reducing the range of the addresses did reduce time.

Sorting the addresses did reduce time, but not by much.

This suggests to me that this is truly seeking and not merely reading and skipping (as a stream tends to). The speed is still terrible at this stage and it makes my hard drive sound like a washing machine, but the code was designed for a quick test and has yet to be made pretty. It may still work fine.

Thanks to both EJP and Apangin for the help. Read more in their respective answers.

Edit:

I have since run my code on a Windows 7 machine (I didn't have one originally), and I get a slightly different exception (see below). This was run with admin privileges, and the first piece of code still works under the same conditions.

java.nio.file.FileSystemException: \\.\PhysicalDrive0\: A device attached to the system is not functioning.

at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)

at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)

at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)

at sun.nio.fs.WindowsFileSystemProvider.newFileChannel(WindowsFileSystemProvider.java:115)

at java.nio.channels.FileChannel.open(FileChannel.java:287)

at java.nio.channels.FileChannel.open(FileChannel.java:335)

at testapp.TestApp.doStuff(TestApp.java:30)

at testapp.TestApp.main(TestApp.java:24)

Edit 2:

In response to EJP, I have tried:

byte[] bytes = new byte[20];

ByteBuffer bb = ByteBuffer.wrap(bytes);

bb.rewind();

File disk = new File("\\\\.\\PhysicalDrive0\\");

try (FileInputStream fis = new FileInputStream(disk);

ReadableByteChannel rbc = Channels.newChannel(new FileInputStream(disk))) {

System.out.println("Channel created");

int read = rbc.read(bb);

System.out.println("Read " + read + " bytes");

System.out.println("No exceptions! Yay!");

} catch (Exception ex) {

System.out.println("Oh bother: " + ex);

}

When I try this I get the following output:

Channel created

Oh bother: java.io.IOException: The parameter is incorrect

So it appears that I can create a FileChannel or ReadableByteChannel, but I can't use it; that is, the error is simply deferred.

解决方案

When accessing physical drive without buffering, you can read only complete sectors. This means, if a sector size is 512 bytes, you can read only multiple of 512 bytes. Change your buffer length to 512 or 4096 (whatever your sector size is) and FileChannel will work fine:

ByteBuffer buf = ByteBuffer.allocate(512);

try (RandomAccessFile raf = new RandomAccessFile("\\\\.\\PhysicalDrive0", "r");

FileChannel fc = raf.getChannel()) {

fc.read(buf);

System.out.println("It worked! Read bytes: " + buf.position());

} catch (Exception e) {

e.printStackTrace();

}

Your original FileInputStream code works obviously because of BufferedInputStream which has the default buffer size of 8192. Take it away - and the code will fail with the same exception.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值