【问题标题】:How to copy directory symbolic link as a link to the target?如何将目录符号链接复制为指向目标的链接?
【发布时间】:2014-06-09 03:32:33
【问题描述】:

比如说,我使用mklink 命令创建了一个目录符号链接,如下所示:

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

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

::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。我尝试提升运行我的进程(只是在不太可能的情况下我需要提升运行),但它仍然给了我相同的错误代码。

【问题讨论】:

  • 测试您是否具有所需的读写访问权限,并且文件具有正确的所有权/模式。无论是链接、文件、目录还是其他,它都被认为是用于 linux/gcc 计算目的的“文件”。所以先检查基础知识。
  • @DavidC.Rankin:我问的是 Windows 操作系统。是的,我拥有该文件的适当所有权。

标签: c++ windows winapi filesystems ntfs


【解决方案1】:

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

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

API 文档中有关于这种行为的微妙提示,其中指出:

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

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

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

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那么你需要定义微软重解析点数据结构(复制自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
}

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

这仍然没有消除成为 (a) 管理员或 (b) 将组策略编辑为 permit non-admin users to create symbolic links 的需要。

【讨论】:

  • 好的。谢谢。我明白为什么我会出错。不过,我的问题仍然存在。如何复制该链接?
  • 您可能需要重新创建它。
  • 你为什么不在你的伪代码示例中添加一个CloseHandle 调用,我会将其标记为答案。它似乎完成了这项工作。这种方法的缺点是需要以管理员身份运行才能正常工作。这是获得CreateSymbolicLink 成功所需的SE_CREATE_SYMBOLIC_LINK_NAME 权限所必需的。
  • 我唯一的怀疑是它的工作方式是解析链接并在目标点创建一个新链接;这意味着如果链接在创建时无法解析,它将无法工作。我将了解如何获取底层重解析点数据并改用它。
  • 我添加了代码以在创建链接时使用底层重解析点数据,这将在链接目标当前不存在的情况下保留链接。您可以编辑组策略以允许普通用户创建符号链接。
【解决方案2】:

我在寻找有关设置了 COPY_FILE_COPY_SYMLINK 标志的 CopyFileEx 行为的答案时发现了这个旧线程。我有执行此操作的代码,它所做的只是创建具有正确名称的空文件。它们不是功能符号链接。我什至以提升的权限运行(代码以管理员身份运行,具有 SE_BACKUP_NAME、SE_RESTORE_NAME 权限——甚至尝试显式添加 SE_CREATE_SYMBOLIC_LINK_NAME),但没有任何变化。

我还使用 /SL 开关运行 Robocopy——它也只是创建了这些无用的空文件。似乎是 CopyFileEx 错误(Windows 10 x64)。

PeteSh 的代码应该可以工作(我还没有尝试过),但遗憾的是 CopyFileEx 没有像宣传的那样工作。

【讨论】:

    猜你喜欢
    • 2020-11-10
    • 2012-03-28
    • 2023-04-04
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-06
    相关资源
    最近更新 更多