硬链接到带有 Win32 API 的符号链接?
Posted
技术标签:
【中文标题】硬链接到带有 Win32 API 的符号链接?【英文标题】:Hard link to a symbolic link with Win32 API? 【发布时间】:2022-01-04 05:16:03 【问题描述】:这个问题的快速背景,因为我相信它会引起一些人的注意:我正在用 C 语言开发一个用于进行备份的命令行工具,并且我正在使用 NTFS 硬链接实现增量备份。因此,如果符号链接存在于先前的备份中,我必须能够指向符号链接本身,而不是目标。
不幸的是,CreateHardLink 的页面明确指出:
符号链接行为——如果路径指向符号链接,则 函数创建到目标的硬链接。
现在我一直在想,解决这个问题的方法是什么?如何创建指向符号链接本身而不是目标的硬链接?我确实注意到 Windows 的内部命令 MKLINK
似乎能够创建指向符号链接的硬链接。所以理论上,我想我可以在 C 中使用 system
函数,但老实说,它感觉很懒,我倾向于避免它。是否有可能只使用 Win32 API 的解决方案?
我还发现了一些来自 Google 开发人员 ([1] [2]) 的代码 sn-ps,其中包含一些有关 CreateHardLink
实现的细节等等,但它似乎对我来说太低级了任何真正的意义。另外,(我可能错了)GitHub repo 中提供的功能似乎只兼容 Windows 10 及更高版本,但我希望至少也支持 Windows 7。
【问题讨论】:
当您已经自行指向准备好的代码时会出现什么问题?使用FILE_FLAG_OPEN_REPARSE_POINT
或FILE_OPEN_REPARSE_POINT
选项打开文件所需的所有内容。而不是使用记录在案的FILE_LINK_INFORMATION
和NtSetInformationFile
不管我怎么看 - 真的 CreateHardLink
创建指向符号链接本身的硬链接,而不是目标。文档是错误的。所以你可以简单地使用CreateHardLink
。 Windows 的内部命令 MKLINK 似乎能够创建指向符号链接的硬链接 - 它也只需调用 CreateHardLink
【参考方案1】:
CreateHardLink
自己创建指向符号链接(重解析点)的硬链接,而不是指向目标。所以文档根本是错误的。 lpExistingFileName
使用选项 FILE_OPEN_REPARSE_POINT
打开
所以你可以简单地使用CreateHardLink
,不需要做更多的事情。反之亦然 - 如果您想创建指向目标的硬链接,您需要自定义实现 CreateHardLink
而不是使用 FILE_OPEN_REPARSE_POINT
(如果您将使用 NtOpenFile
)或 FILE_FLAG_OPEN_REPARSE_POINT
如果您使用 CreatFileW
)
我确实注意到 Windows 的内部命令 MKLINK 似乎能够 创建符号链接的硬链接。
如果您使用mklink
命令调试cmd.exe,您会很容易注意到也简单地调用了CreateHardLinkW
api(为其设置断点)
创建符号链接文件的硬链接后,您可以在资源管理器中查看该文件类型为 .symlink 。对于测试,如果硬链接指向目标,我们可以从目标文件中删除链接(通过使用FSCTL_DELETE_REPARSE_POINT
) - 任何使用符号链接的操作都不会影响硬链接。但是如果我们创建了指向符号链接自身的硬链接——在中断符号链接之后——硬链接也将被打破:
void TestCreateHardLink(PCWSTR lpFileName, PCWSTR lpSymlinkFileName, PCWSTR lpExistingFileName)
if (CreateSymbolicLinkW(lpSymlinkFileName, lpExistingFileName, 0))
if (CreateHardLinkW(lpFileName, lpSymlinkFileName, 0))
HANDLE hFile = CreateFileW(lpSymlinkFileName, FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (hFile != INVALID_HANDLE_VALUE)
REPARSE_DATA_BUFFER rdb = IO_REPARSE_TAG_SYMLINK ;
OVERLAPPED ov ;
if (DeviceIoControl(hFile, FSCTL_DELETE_REPARSE_POINT, &rdb, sizeof(rdb), 0, 0, 0, &ov))
MessageBoxW(0, 0, 0, 0);
CloseHandle(hFile);
DeleteFileW(lpFileName);
DeleteFileW(lpSymlinkFileName);
我们想要更灵活地实现硬链接创建(设置目标),可以使用下一个代码:
HRESULT CreateHardLinkExW(PCWSTR lpFileName, PCWSTR lpExistingFileName, BOOLEAN ReplaceIfExisting, BOOLEAN bToTarget)
HANDLE hFile = CreateFileW(lpExistingFileName, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING,
bToTarget ? FILE_FLAG_BACKUP_SEMANTICS : FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (hFile == INVALID_HANDLE_VALUE)
return HRESULT_FROM_WIN32(GetLastError());
UNICODE_STRING NtName;
NTSTATUS status = RtlDosPathNameToNtPathName_U_WithStatus(lpFileName, &NtName, 0, 0);
if (0 <= status)
ULONG Length = FIELD_OFFSET(FILE_LINK_INFORMATION, FileName) + NtName.Length;
if (PFILE_LINK_INFORMATION LinkInfo = (PFILE_LINK_INFORMATION)_malloca(Length))
LinkInfo->ReplaceIfExists = ReplaceIfExisting;
LinkInfo->RootDirectory = 0;
LinkInfo->FileNameLength = NtName.Length;
memcpy(LinkInfo->FileName, NtName.Buffer, NtName.Length);
IO_STATUS_BLOCK iosb;
status = NtSetInformationFile(hFile, &iosb, LinkInfo, Length, FileLinkInformation);
else
status = STATUS_NO_MEMORY;
RtlFreeUnicodeString(&NtName);
CloseHandle(hFile);
return 0 > status ? HRESULT_FROM_NT(status) : S_OK;
【讨论】:
嗯,这绝对教会了我不要相信文档。奇怪的是,即使symbolic links 的页面也说它链接到符号链接目标。猜猜它一定是复制粘贴...无论如何,为了验证操作系统版本之间的行为没有改变,我在 Windows 7 和 10 上测试了CreateHardLinkW
,确实,它确实到符号链接的硬链接(不是符号链接的目标,如文档所示)。谢谢!
@user966939 我在 xp 上看这个,甚至这里是在符号链接上创建的硬链接,而不是在目标上
您是如何在 XP 上创建符号链接的?我以为他们不是XP的东西?我自己也想去那里看看。
@user966939 符号链接(带有标签IO_REPARSE_TAG_SYMLINK
的重新解析点)可以在xp 上创建,但系统不理解此标签并且链接不起作用。但任务不在此 - 我说 即使这里是在符号链接上创建的硬链接,而不是在目标上,这是真的 - 只需在 xp 上查找 CreateHardLinkW
并容易看到该文件(我们尝试在其上创建硬链接)使用FILE_OPEN_REPARSE_POINT
选项打开。这意味着硬链接将直接针对该文件,而不是针对目标以上是关于硬链接到带有 Win32 API 的符号链接?的主要内容,如果未能解决你的问题,请参考以下文章