如何使用 Java 的 IO 读取 Windows NTFS 的备用数据流?

How do I read Windows NTFS's Alternate Data Stream using Java's IO?

我正在尝试让我的 Java 应用程序读取给定路径中的所有数据。所以文件、目录、元数据等。这还包括 NTFS 称为备用数据流 (ADS) 的一种奇怪的东西。

显然它就像目录或文件中的第二层数据。您可以打开命令提示符并使用“:”在 ADS 中创建一个文件,例如:

C:\ADSTest> echo test>:ads.txt

所以,

C:\ADSTest> notepad :ads.txt

应该打开一个记事本,其中包含字符串 "test"。但是,如果您这样做了:

C:\ADSTest> dir

您将看不到 ads.txt。但是,如果您使用显示 ADS 数据的 dir 选项,您将能够看到它:

C:\ADSTest> dir /r
MM/dd/yyyy hh:mm            .:ads.txt

现在,我知道 Java IO 具有读取 ADS 的能力。我怎么知道的?嗯,Oracle's documentations clearly states so:

If the file attributes supported by your file system implementation aren't sufficient for your needs, you can use the UserDefinedAttributeView to create and track your own file attributes.

Some implementations map this concept to features like NTFS Alternative Data Streams and extended attributes on file systems such as ext3 and ZFS.

此外,a random post on a random forum :D

The data is stored in NTFS Alternate data streams (ADS) which are readable through Java IO (I have tested it).

问题是,我找不到任何可以解析ADS的预写文件属性查看器,而且我不明白如何编写自己的ADS解析器。我是一名初级程序员,所以我觉得这太过头了。有人能帮我出出主意或给我指明正确的方向吗?

解决方案

编辑: 在@knosrtum 的帮助下,我能够编造一个方法,该方法将 return 来自给定路径的所有解析的 ADS 信息作为 ArrayList字符串(它也可以很容易地编辑为文件的 ArrayList)。以下是可能需要它的任何人的代码:

public class ADSReader {

    public static ArrayList<String> start(Path toParse) {

        String path = toParse.toString();
        ArrayList<String> parsedADS = new ArrayList<>();

        final String command = "cmd.exe /c dir " + path + " /r"; // listing of given Path.

        final Pattern pattern = Pattern.compile(
                "\s*"                 // any amount of whitespace
                        + "[0123456789,]+\s*"   // digits (with possible comma), whitespace
                        + "([^:]+:"    // group 1 = file name, then colon,
                        + "[^:]+:"     // then ADS, then colon,
                        + ".+)");      // then everything else.

        try {
            Process process = Runtime.getRuntime().exec(command);
            process.waitFor();
            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;

                while ((line = br.readLine()) != null) {
                    Matcher matcher = pattern.matcher(line);
                    if (matcher.matches()) {
                        parsedADS.add((matcher.group(1)));
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int z = 0; z<parsedADS.size(); z++)
            System.out.println(parsedADS.get(z));

        return parsedADS;

    }
}

我能够通过使用语法 "file_name:stream_name" 打开文件来读取文件的 ADS。所以如果你这样做了:

C:>echo Hidden text > test.txt:hidden

那么你应该可以这样做:

package net.snortum.play;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class AdsPlay {
    public static void main(String[] args) {
        new AdsPlay().start();
    }

    private void start() {
        File file = new File("test.txt:hidden");
        try (BufferedReader bf = new BufferedReader( new FileReader(file))) {
            String hidden = bf.readLine();
            System.out.println(hidden);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如果你想从dir /r命令获取ADS数据,我想你只需要执行一个shell并捕获输出:

package net.snortum.play;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExecPlay {

    public static void main(String[] args) {
        new ExecPlay().start();
    }

    private void start() {
        String fileName = "not found";
        String ads = "not found";
        final String command = "cmd.exe /c dir /r"; // listing of current directory

        final Pattern pattern = Pattern.compile(
                  "\s*"                 // any amount of whitespace
                + "[0123456789,]+\s*"   // digits (with possible comma), whitespace
                + "([^:]+):"             // group 1 = file name, then colon
                + "([^:]+):"             // group 2 = ADS, then colon
                + ".+");                 // everything else

        try {
            Process process = Runtime.getRuntime().exec(command);
            process.waitFor();
            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;

                while ((line = br.readLine()) != null) {
                    Matcher matcher = pattern.matcher(line);
                    if (matcher.matches()) {
                        fileName = matcher.group(1);
                        ads = matcher.group(2);
                        break;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(fileName + ", " + ads);

    }
}

现在您可以使用第一个代码清单来打印 ADS 数据了。