如何将目录符号链接复制为目标链接?

时间:2014-06-09 03:32:34

标签: c++ windows winapi filesystems ntfs

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

2 个答案:

答案 0 :(得分:2)

您要求使用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,那么您需要定义Microsoft重新分析点数据结构(从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

答案 1 :(得分:0)

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

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

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