硬链接到带有 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_POINTFILE_OPEN_REPARSE_POINT 选项打开文件所需的所有内容。而不是使用记录在案的FILE_LINK_INFORMATIONNtSetInformationFile 不管我怎么看 - 真的 CreateHardLink 创建指向符号链接本身的硬链接,而不是目标。文档是错误的。所以你可以简单地使用CreateHardLinkWindows 的内部命令 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 的符号链接?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建符号链接或硬链接到另一个符号链接?

符号链接和硬链接有啥区别?

硬链接和软连接(符号链接)

Linux中有两种不同的文件链接类别。 符号链接(又称软链接) 硬链接

如何列出 NTFS 文件系统上的所有符号链接

Linux创建文件链接(ln | 符号链接和硬链接)