如何以编程方式访问Windows符号链接的目标路径?

14

Windows 6 (Vista和Server 2008)支持适当的符号链接,可以通过CreateSymbolicLink函数创建。但是似乎没有相应的函数来查询符号链接以获取链接目标的路径。

我发现符号链接是重解析点的一种实现方式,因此可以使用重解析点函数来获取目标路径。但是,我需要使用重解析点的头文件似乎随Windows Driver Kit一起提供。在VS2008中设置这个kit似乎是一个不容易的任务。

是否有我错过的简单好用的函数来获取链接的目标,还是我真的必须设置一个Windows驱动程序开发环境才能编写代码访问这些信息?

编辑:Adam Mitz提出了GetFinalPathNameByHandle的建议。该函数对于本地符号链接非常有效,但似乎无法解决通过UNC路径解析远程链接的问题。

编辑2:根据Adam的要求,以下是我尝试过的更多细节:

我最初采用了FSCTL_GET_REPARSE_POINT / DeviceIoControl方法,但这会得到一个REPARSE_DATA_BUFFER结构。定义此结构的标头似乎仅存在于Windows Driver Kit中。

当链接存在于本地磁盘(如C:\ ... \ link等)时,GetFinalPathNameByHandle()函数运行良好。有趣的是,我发现无论是否指定了FILE_FLAG_OPEN_REPARSE_POINT标志,无论目标文件是否存在,都可以使用CreateFileW()获取链接的句柄-从而获取目标。

当使用CreateFileW()GetFinalPathNameByHandle()查询远程链接时(\\?\UNC\....),事情开始变得复杂。如果指定了FILE_FLAG_OPEN_REPARSE_POINTGetFinalPathNameByHandle()总是返回链接路径,而非目标路径。如果未指定FILE_FLAG_OPEN_REPARSE_POINT,则仅当目标存在且在与链接相同的计算机上时,才会返回目标路径。如果链接指向另一台计算机,我会收到网络权限错误。如果链接指向本地不存在的文件,则会出现文件未找到错误。


请澄清符号链接本身是否位于远程服务器上(通过UNC),还是符号链接目标是UNC路径。 - Adam Mitz
此外,我认为您不需要DDK来读取重分析点(没有“解析点”这样的东西)。请参见CreateFile上的FILE_FLAG_OPEN_REPARSE_POINT标志或DeviceIoControl的FSCTL_GET_REPARSE_POINT标志。这些是否有帮助取决于您对我之前评论的回答。 - Adam Mitz
资源管理器在某种程度上做到了这一点,我怀疑它并不是直接通过发送IO_CTL实现的。试试吧:创建一个目录符号链接,打开属性,你会看到一个"快捷方式"标签,其目标将会被灰化。 - Fowl
2个回答

12

GetFinalPathNameByHandle

所谓最终路径(final path),是指在完全解析路径后返回的路径。例如,对于名为“C:\tmp\mydir”的符号链接,它指向的是“D:\yourdir”,那么最终的文件系统路径应该是“D:\yourdir”。


@Adam,不幸的是,虽然GetFinalPathNameByHandle()适用于本地符号链接,但对于基于远程UNC的符号链接却返回链接,而非目标。因此,我已经“重新打开”了这个问题。 - David Arno
我认为我按照问题的要求回答了(就像你的JNI/Mock问题一样...呃)。也许这是一个新问题。 - Adam Mitz
GetFinalPathNameByHandle基本上就是答案,如果只想使用本地链接的话。但是对于远程链接来说,这个函数似乎存在一个bug。因此我再次将其标记为正确答案,并记录了这个警告。 - David Arno
很遗憾,此功能不支持在Win XP上。 - user13947194
这个答案是不正确的。如果句柄引用了一个具有多个路径(硬链接)的文件,那么无论符号链接实际指向哪个路径,只会返回其中一个路径。结果是得到一个完全不同的路径,而不是符号链接中实际的路径。此外,符号链接可以是相对或绝对的,而该函数只提供绝对答案。 - LB--

1
符号链接可以是绝对路径或相对路径,如果它们指向具有多个路径(硬链接)的文件,则其他答案中提到的GetFinalPathNameByHandle函数通常会给出错误的答案。如果您打开符号链接本身,则只会得到符号链接本身的路径,如果您打开符号链接指向的路径,则即使符号链接指向其不同的硬链接,您也只能得到该文件的一个路径。如果文件不存在,则根本无法使用该函数。

正确的答案似乎是使用C++17的std::filesystem::read_symlink函数,这实际上将读取重解析点数据并为您提供符号链接包含的确切路径,无论是绝对还是相对,并且即使目标路径不存在也可以正常工作。此外,它在UWP应用程序中也可以工作,尽管底层Windows API显然不受UWP应用程序支持。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接