【问题标题】:Determine whether a file is a junction (in Windows) or not?确定文件是否是联结(在 Windows 中)?
【发布时间】:2012-11-23 20:55:10
【问题描述】:

我一直在寻找一种方法来确定文件是否为连接,但没有找到任何令人满意的答案。

我尝试的第一件事是:

Files.isSymbolicLink(aPath)

它只检测符号链接而不是 Windows 中称为联结的文件。

还尝试了此处提出的解决方案(使用 JNA 库): Stackoverflow question (3249117) ,但它从来没有在我知道是联结的任何文件上返回 true。

我发现确定哪些文件是联结的唯一方法是在 windows 命令提示符下运行以下命令:

DIR /S /A:L

在我的计算机上它返回 66 个文件夹,而 Files.isSymbolicLink(aPath) 只返回 2 个。 所以我想我可以找到一种方法来利用它,但我认为在遍历文件树时它不会非常有效。

有没有什么方法可以使用标准的 java 库或者 JNA 来做到这一点?

【问题讨论】:

    标签: java windows winapi jna


    【解决方案1】:

    如果您有正确的 java,例如 Oracle jdk 8,则可以在没有 JNA 的情况下做到这一点。它很狡猾,它可以停止工作,但是....

    可以获取链接相关的BasicFileAttributes接口:

    BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
    

    这个接口实现可能是一个类 sun.nio.fs.WindowsFileAttributes。这个类有一个方法isReparsePoint,它对连接点和符号链接都返回true。所以可以尝试使用反射并调用方法:

        boolean isReparsePoint = false;
        if (DosFileAttributes.class.isInstance(attr))
            try {
                Method m = attr.getClass().getDeclaredMethod("isReparsePoint");
                m.setAccessible(true);
                isReparsePoint = (boolean) m.invoke(attr);
            } catch (Exception e) {
                // just gave it a try
            }
    

    现在你只能发现它是否真的是符号链接:Files.isSymbolicLink(path)

    如果不是,但它是重解析点,那就是结。

    【讨论】:

    • 您可以通过反思检查WindowsFileAttributesparseTag 字段来节省Files.isSylmbolicLink(path) 步骤的需要,Junction 的值将是-1610612733(和符号链接-1610612724),只是将 try 块中的内容替换为:Field field = attr.getClass().getDeclaredField("reparseTag"); field.setAccessible(true); int parseTag = (int) field.get(attr); boolean isJunction = parseTag == -1610612733;
    【解决方案2】:

    如果你可以在 JNA 中编写原生代码,你可以直接调用 Win32 API GetFileAttributes() 函数并检查 FILE_ATTRIBUTE_REPARSE_POINT 标志(连接被实现为重解析点)。

    更新:要区分不同类型的重解析点,您必须检索实际重解析点的ReparseTag。对于连接点,它将被设置为IO_REPARSE_TAG_MOUNT_POINT (0xA0000003)。

    有两种方法可以获取ReparseTag

    1. 使用DeviceIoControl()FSCTL_GET_REPARSE_POINT 控制代码来获得REPARSE_DATA_BUFFER 结构,它作为ReparseTag 字段。您可以在以下文章中看到使用此技术的 IsDirectoryJunction() 实现示例:

      NTFS Hard Links, Directory Junctions, and Windows Shortcuts

    2. 使用FindFirstFile() 获取WIN32_FIND_DATA 结构。如果路径具有FILE_ATTRIBUTE_REPARSE_POINT 属性,则dwReserved0 字段将包含ReparseTag

    【讨论】:

    • 谢谢,除了复制和粘贴代码之外,之前从未真正使用过 JNA,但如果这是我想我最好学习的唯一方法。
    • 您的建议似乎与我发布的链接中的建议相似,如果您能给我提供一个如何检查 reparse-flag 的示例,将不胜感激。
    • FILE_ATTRIBUTE_REPARSE_POINT 被定义为0x400,因此您链接到的示例代码专门检查FILE_ATTRIBUTE_REPARSE_POINT
    • 这不会只检测到路口。它检测所有重解析点。连接点只是重解析点的一个示例。
    • 在 Windows 中,连接点被实现为重解析点。要区分不同类型的重解析点,您必须使用DeviceIoControl(FSCTL_GET_REPARSE_POINT)。返回的REPARSE_DATA_BUFFER 结构有一个ReparseTag 字段(它设置为IO_REPARSE_TAG_MOUNT_POINT 用于连接点)。
    【解决方案3】:

    在 J2SE 1.7 中使用 Java NIO

    /**
    * returns true if the Path is a Windows Junction
    */
    private static boolean isJunction(Path p) {
        boolean isJunction = false;
        try {
            isJunction = (p.compareTo(p.toRealPath()) != 0);
        } catch (IOException e) {
            e.printStackTrace(); // TODO: handleMeProperly
        }
        return isJunction;
    }
    

    【讨论】:

    • 好像不行。 toRealPath() 与路口的路径本身相同。
    • 这是预期在一个路口。
    • 所以这段代码不起作用,是吗?对于一个路口 p.compareTo(p.toRealPath()) 是 0 正如你所同意的那样,正如我在测试中看到的那样。
    • @Peter 为什么会这样?尽管流行的观点,连接点不是硬链接。 NTFS 有硬链接、连接和符号链接。符号链接和联结之间的区别与遍历 UNC 目标有关,而联结做得不好。
    • 我更新了上面提到的要点,还包括一个硬链接示例(仅适用于文件)。所以这些可以通过 Files.isRegularFile() 来区分。但上述方法对 Junctions 工作正常。你能给我一个例子,你所说的遍历 UNC 目标是什么意思。
    【解决方案4】:

    虽然在 Windows 上,联结的属性具有 isSymbolicLink() == false,但它们具有 isOther() == true。所以你可以这样做:

    boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows")
    BasicFileAttributes attrs = Files.readAttributes(aPath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
    boolean isJunction = isWindows && attrs.isDirectory() && attrs.isOther();
    
    【解决方案5】:

    黑盒解决方案:

    aPath.toRealPath() 解析连接和符号链接,所以结果会与aPath 有偏差。

    此外,BasicFileAttributes.isSymbolicLink() 提供 false 用于未记录原因的路口:

    例如Path.toRealPath(LinkOption.NOFOLLOW_LINKS) 将联结视为链接,但无法解决它!

    因此,通过 toRealPath()BasicFileAttributes.isSymbolicLink() 的非身份,您可以识别一个路口。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-26
      • 2011-03-14
      • 1970-01-01
      • 2016-07-31
      • 2012-11-16
      • 1970-01-01
      • 2011-02-12
      • 1970-01-01
      相关资源
      最近更新 更多