如何将目录符号链接复制为指向目标的链接? [英] How to copy directory symbolic link as a link to the target?

查看:75
本文介绍了如何将目录符号链接复制为指向目标的链接?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比如说,我使用 mklink 命令创建了一个目录符号链接像这样:

Say, I created a directory symbolic link using the mklink command as such:

mklink/d "test dir link1" "dest dir"

mklink /d "test dir link1" "dest dir"

如何将其复制为目标目录的链接?当我尝试使用 CopyFileEx API 和 COPY_FILE_COPY_SYMLINK 标志如下:

How can I copy it as a link to the target directory? When I try to use the CopyFileEx API and the COPY_FILE_COPY_SYMLINK flag as such:

::CopyFileEx(L"D:\\Path to source\\test dir link1",
    L"D:\\Path to destination\\test dir link1 copy",
    NULL, NULL, NULL, 
    COPY_FILE_COPY_SYMLINK);

它返回错误代码ERROR_ACCESS_DENIED.

附注.我尝试运行我的进程提升(只是在不太可能的情况下我需要运行提升),但它仍然给了我相同的错误代码.

PS. I tried running my process elevated (just in unlikely scenario that I need to run elevated) and it still gave me the same error code.

推荐答案

您要求使用 CopyFileEx API 复制目录.它是一个符号链接的事实直到检查它是一个目录之后才会得到解析,这意味着您不能使用此 API 来复制目录符号链接.

You're asking to copy a directory using the CopyFileEx API. The fact that it's a symbolic link doesn't get resolved until after the check that it's a directory, which means you can't use this API to copy a directory symlink.

目录符号链接与文件符号链接的处理方式略有不同 - 创建它们时,您必须将额外的参数传递给 CreateSymbolicLink API.

Directory symlinks are treated slightly differently from file symlinks - when you create them you have to pass in an extra parameter to the CreateSymbolicLink API.

API 文档中有一个关于此行为的微妙提示,其中指出:

There is a subtle hint as to this behavior in the API documentation, where it states:

要删除符号链接,请删除文件(使用 DeleteFile 或类似的 API)或删除目录(使用 RemoveDirectory 或类似的 API),具体取决于使用的符号链接类型.

To remove a symbolic link, delete the file (using DeleteFile or similar APIs) or remove the directory (using RemoveDirectory or similar APIs) depending on what type of symbolic link is used.

这强烈表明适用于文件的 API 不适用于目录符号链接.

This strongly indicates that APIs that work on files will not work on directory symlinks.

现在关于如何复制它,我强烈希望您必须再次创建符号链接,此过程是读取符号链接目标,然后在目标中再次创建链接;类似于(根本没有错误处理,将相对链接转换为绝对链接):

Now as to how to copy it, I strongly expect you'll have to create the symbolic link again, the process for this is to read the symlink target and then create the link a second time in the destination; something akin to (no error handling at all, transforms relative links to absolute links):

HANDLE h = CreateFile(srcFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
TCHAR outbuffer[2048];
DWORD written = GetFinalPathNameByHandle(h, outbuffer, 2048, 0);
CreateSymbolicLink(targetFile, outbuffer, SYMBOLIC_LINK_FLAG_DIRECTORY);
CloseHandle(h);

现在要使用底层重解析点数据创建符号链接,事情变得有点复杂.如果您没有 ntifs.h,那么您需要定义 Microsoft 重新分析点数据结构(从 REPARSE_DATA_STRUCTURE 的 MSDN 页面):

Now to create the symlink using the underlying reparse point data things get a little more complicated. If you don't have ntifs.h then you need to define the Microsoft reparse point data structure (copied from the MSDN page for REPARSE_DATA_STRUCTURE):

typedef struct _REPARSE_DATA_BUFFER {
  ULONG  ReparseTag;
  USHORT ReparseDataLength;
  USHORT Reserved;
  union {
    struct {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      ULONG  Flags;
      WCHAR  PathBuffer[1];
    } SymbolicLinkReparseBuffer;
    struct {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      WCHAR  PathBuffer[1];
    } MountPointReparseBuffer;
    struct {
      UCHAR DataBuffer[1];
    } GenericReparseBuffer;
  };
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;

我们改变常规:

HANDLE h = CreateFile(srcFile, 0,
  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
  OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (h != INVALID_HANDLE_VALUE) {
  char tmpBuffer[32 * 1024];
  REPARSE_DATA_BUFFER *repBuffer = reinterpret_cast<REPARSE_DATA_BUFFER *>(tmpBuffer);
  DWORD retBytes;
  if (DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, 0, 0, (void *)tmpBuffer,
      sizeof tmpBuffer, &retBytes, 0)) {
    if (repBuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
      wchar_t dest[2048];
      memcpy(dest, repBuffer->SymbolicLinkReparseBuffer.PathBuffer +
                   repBuffer->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t),
                   repBuffer->SymbolicLinkReparseBuffer.PrintNameLength);
      dest[repBuffer->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t)] = 0;
      if (!CreateSymbolicLink(targetFile, dest, SYMBOLIC_LINK_FLAG_DIRECTORY)) {
        // Error Handling
      }
    }
  }
  CloseHandle(h);
} else {
  // Open Error Handling
}

它要复杂得多,但它保留了其底层形式的符号链接,即如果它是一个相对链接,那么创建的链接也将是一个相对链接.此外,如果目标不存在,链接仍然会被创建.

It's significantly more complicated, but it preserves the symbolic link in it's underlying form, i.e. if it's a relative link, then the created link will also be a relative link. In addition if the target does not exist the link still gets created.

这仍然没有消除成为 (a) 管理员或 (b) 将组策略编辑为 允许非管理员用户创建符号链接.

This still does not remove the need to be either (a) an administrator, or (b) edited the group policy to permit non-admin users to create symbolic links.

这篇关于如何将目录符号链接复制为指向目标的链接?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆