用 Win32 API 硬 link 到符号 link?
Hard link to a symbolic link with Win32 API?
这个问题的快速背景知识,因为我相信它会引起一些人的注意:我正在用 C 语言开发一个命令行工具来进行备份,并且我正在使用 NTFS 硬盘实现增量备份 links。因此,如果符号 links 存在于先前的备份中,我必须能够指向符号 links 本身,而不是目标。
不幸的是,CreateHardLink 的页面明确指出:
Symbolic link behavior—If the path points to a symbolic link, the
function creates a hard link to the target.
现在我一直在想,解决这个问题的方法是什么?我如何创建一个指向符号 link 本身而不是目标的 hardlink?我确实注意到 Windows' 内部命令 MKLINK
似乎能够创建 hardlinks 到 symlinks。所以理论上,我想我可以在 C 中使用 system
函数,但说实话,它感觉很懒,我倾向于避免它。是否有仅使用 Win32 的解决方案 API?
我还看到了一些来自 Google 开发人员 ([1] [2]) 的代码片段,其中有一些关于 CreateHardLink
实现的细节等等,但似乎有点太低了让我真正理解它的水平。此外,(我对此可能是错误的)GitHub 存储库中提供的功能似乎只与 Windows 10 及更高版本兼容,但我希望至少支持 Windows 7也是。
CreateHardLink
创建硬 link 到符号 link(重新分析点)本身,而不是目标。所以文档是错误的。 lpExistingFileName
使用选项 FILE_OPEN_REPARSE_POINT
打开
所以你可以简单地使用 CreateHardLink
并且不需要做更多的事情。反之亦然 - 如果你想创建硬 link 目标,你需要 CreateHardLink
的自定义实现而不是使用 FILE_OPEN_REPARSE_POINT
(如果你将使用 NtOpenFile
)或 FILE_FLAG_OPEN_REPARSE_POINT
如果你使用 CreatFileW
)
I did notice Windows' internal command MKLINK appears to be able to
create hardlinks to symlinks.
如果你用 mklink
命令调试 cmd.exe 你会很容易注意到 CreateHardLinkW
api 调用(设置断点)
在创建 hardlink 到 symlink 文件后,您可以在资源管理器中查看文件类型为 .symlink 。对于测试,如果 hardlink 指向目标,我们可以从目标文件中删除 link(通过使用 FSCTL_DELETE_REPARSE_POINT
)——任何使用 symlink 的操作都不会影响 hardlink .但是如果我们自己创建 hardlink 到 symlink - 在 break symlink 之后 - hard link 也会被 break:
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);
}
}
我们想要更灵活的实现hardlink create(set target),可以使用下一个代码:
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;
}
这个问题的快速背景知识,因为我相信它会引起一些人的注意:我正在用 C 语言开发一个命令行工具来进行备份,并且我正在使用 NTFS 硬盘实现增量备份 links。因此,如果符号 links 存在于先前的备份中,我必须能够指向符号 links 本身,而不是目标。
不幸的是,CreateHardLink 的页面明确指出:
Symbolic link behavior—If the path points to a symbolic link, the function creates a hard link to the target.
现在我一直在想,解决这个问题的方法是什么?我如何创建一个指向符号 link 本身而不是目标的 hardlink?我确实注意到 Windows' 内部命令 MKLINK
似乎能够创建 hardlinks 到 symlinks。所以理论上,我想我可以在 C 中使用 system
函数,但说实话,它感觉很懒,我倾向于避免它。是否有仅使用 Win32 的解决方案 API?
我还看到了一些来自 Google 开发人员 ([1] [2]) 的代码片段,其中有一些关于 CreateHardLink
实现的细节等等,但似乎有点太低了让我真正理解它的水平。此外,(我对此可能是错误的)GitHub 存储库中提供的功能似乎只与 Windows 10 及更高版本兼容,但我希望至少支持 Windows 7也是。
CreateHardLink
创建硬 link 到符号 link(重新分析点)本身,而不是目标。所以文档是错误的。 lpExistingFileName
使用选项 FILE_OPEN_REPARSE_POINT
打开
所以你可以简单地使用 CreateHardLink
并且不需要做更多的事情。反之亦然 - 如果你想创建硬 link 目标,你需要 CreateHardLink
的自定义实现而不是使用 FILE_OPEN_REPARSE_POINT
(如果你将使用 NtOpenFile
)或 FILE_FLAG_OPEN_REPARSE_POINT
如果你使用 CreatFileW
)
I did notice Windows' internal command MKLINK appears to be able to create hardlinks to symlinks.
如果你用 mklink
命令调试 cmd.exe 你会很容易注意到 CreateHardLinkW
api 调用(设置断点)
在创建 hardlink 到 symlink 文件后,您可以在资源管理器中查看文件类型为 .symlink 。对于测试,如果 hardlink 指向目标,我们可以从目标文件中删除 link(通过使用 FSCTL_DELETE_REPARSE_POINT
)——任何使用 symlink 的操作都不会影响 hardlink .但是如果我们自己创建 hardlink 到 symlink - 在 break symlink 之后 - hard link 也会被 break:
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);
}
}
我们想要更灵活的实现hardlink create(set target),可以使用下一个代码:
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;
}