无法获取 OneDrive 文件夹的重分析点信息
Can't Get Reparse Point Information for the OneDrive Folder
我正在使用下面的代码在我的应用程序中检索重分析点信息。这对符号链接和联结非常有用,但对于 OneDrive 文件夹及其所有子项,'Not a reparse point' 会失败。
using (SafeFileHandle srcHandle = NativeMethods.CreateFile(@"C:\Users\UserName\OneDrive",
0,
System.IO.FileShare.Read,
IntPtr.Zero,
System.IO.FileMode.Open,
NativeMethods.FileFlags.BackupSemantics | NativeMethods.FileFlags.OpenReparsePoint,
IntPtr.Zero))
{
if (!srcHandle.IsInvalid)
{
NativeMethods.REPARSE_DATA_BUFFER rdb = new NativeMethods.REPARSE_DATA_BUFFER();
IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(rdb) + sizeof(uint) + sizeof(ushort) + sizeof(ushort) + 0xFFFF);
var outBufferSize = Marshal.SizeOf(typeof(NativeMethods.REPARSE_DATA_BUFFER));
var outBuffer = Marshal.AllocHGlobal(outBufferSize);
// Determine if it's a symbolic link or a junction point
try
{
int bytesRet = 0;
if (NativeMethods.DeviceIoControl(srcHandle, NativeMethods.FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, outBuffer, outBufferSize, ref bytesRet, IntPtr.Zero) != 0)
{
rdb = (NativeMethods.REPARSE_DATA_BUFFER)Marshal.PtrToStructure(pMem, rdb.GetType());
...
}
else // Fails with ERROR_NOT_A_REPARSE_POINT** (0x1126) on OneDrive folder and all it's child items
{
log.LogError("FSCTL_GET_REPARSE_POINT error=" + Marshal.GetHRForLastWin32Error());
}
}
catch (Exception e1)
{
log.LogError("FSCTL_GET_REPARSE_POINT exception error=" + e1.Message + " -> GetLastWin32Error=" + Marshal.GetLastWin32Error().ToString());
}
finally
{
Marshal.FreeHGlobal(pMem);
}
}
}
原生声明:
[DllImport("kernel32.dll", EntryPoint = "CreateFile", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(string fileName, FileAccessAPI desiredAccess, FileShare shareMode, IntPtr secAttrib, FileMode createDisp, FileFlags flags, IntPtr template);
public const int FSCTL_GET_REPARSE_POINT = 0x000900A8;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern unsafe int DeviceIoControl(SafeFileHandle hFile,
int control,
IntPtr inbuffer,
int bufferSize,
IntPtr outBuffer,
int outBufferSize,
ref int bytesRet,
IntPtr overlapped);
public const uint RP_SYMBOLICLINK = 0xA000000C;
public const uint RP_JUNCTION = 0xA0000003;
public const uint RP_REPARSETAG_WCI = 0x80000018;
public const uint RP_REPARSETAG_APP = 0x8000001b;
public const uint RP_CLOUD = 0x9000001A;
public const uint RP_CLOUD_1 = 0x9000101A;
...
[StructLayout(LayoutKind.Sequential)]
public struct REPARSE_DATA_BUFFER
{
public uint ReparseTag;
public ushort ReparseDataLength;
public ushort Reserved;
public ushort SubstituteNameOffset;
public ushort SubstituteNameLength;
public ushort PrintNameOffset;
public ushort PrintNameLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xFFF0)]
public byte[] PathBuffer;
}
[Flags()]
public enum FileFlags : uint
{
...
OpenReparsePoint = 0x00200000,
BackupSemantics = 0x02000000,
}
以下命令可以成功检索 OneDrive 文件夹的重分析点信息。
fsutil reparsepoint 查询 C:\Users\UserName\OneDrive
如果能确定如何让这段代码发挥作用,那将非常有用。非常令人沮丧的是,已确认具有重新分析点的文件夹会收到一条错误消息,表明它们没有。
我也在 C++ 中尝试过,但得到了同样的错误。
使用一些相关的 API 对其进行了测试。如有问题,欢迎指出。
使用GetFileAttributes
得到的文件属性:
FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY
但没有属性:FILE_ATTRIBUTE_REPARSE_POINT
。而OneDrive中的普通文件只有属性:
FILE_ATTRIBUTE_ARCHIVE
因此,OneDrive 文件夹及其所有子项没有重新分析点 属性。
这里是测试用例:
#include <windows.h>
#include <iostream>
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;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;
int main()
{
DWORD attr = GetFileAttributes(TEXT("C:\Users\UserName\OneDrive"));
if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
printf("with Attributes: FILE_ATTRIBUTE_REPARSE_POINT\n");
else
printf("without FILE_ATTRIBUTE_REPARSE_POINT, Attributes = %x\n",attr);
HANDLE hFile = CreateFile(
TEXT("C:\Users\UserName\OneDrive"),
0,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS| FILE_FLAG_OPEN_REPARSE_POINT,
NULL
);
if (hFile != INVALID_HANDLE_VALUE)
{
REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)malloc(sizeof(REPARSE_DATA_BUFFER)+ sizeof(ULONG)+sizeof(USHORT)+0xffff);
if (rdb)
{
DWORD outBufferSize = sizeof(REPARSE_DATA_BUFFER) + sizeof(ULONG) + sizeof(USHORT) + 0xffff;
DWORD bytesRet = 0;
if (DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, rdb, outBufferSize, &bytesRet, NULL))
wprintf(L"DeviceIoControl succeed! printfname = %s\n", rdb->MountPointReparseBuffer.PathBuffer[rdb->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)]);
else
wprintf(L"error code = %d\n", GetLastError());
free(rdb);
rdb = NULL;
}
else
printf("malloc failed\n");
}
}
并且使用fsutil reparsepoint query "C:\Users\UserName\OneDrive"
,输出总是如下:
基本没有可用的信息。此外,关闭 "Files On Demand" 将删除重新分析点。
编辑:
不同于GetFileAttributes
,FindFirstFile
can get this FILE_ATTRIBUTE_REPARSE_POINT
attribute. According to the document Reparse Point Tags:
To retrieve the reparse point tag, use the FindFirstFile
function.
If the dwFileAttributes
member includes the
FILE_ATTRIBUTE_REPARSE_POINT
attribute, then the dwReserved0
member specifies the reparse point.
更新:
经与相关工程师确认后,此问题由于云文件会隐藏重解析信息,下面是相同的文档
为了克服这个问题,他们有两种方法。
- 将执行文件放在%systemroot%下
- 在调用reparse相关之前先调用下面几行代码activity
typedef NTSYSAPI CHAR(*PGNSI)(CHAR Mode);
#define PHCM_EXPOSE_PLACEHOLDERS ((CHAR)2)
HMODULE hmod = LoadLibrary(L"ntdll.dll");
if (hmod == NULL)
{
wprintf(L"LoadLibrary failed with %u\n", GetLastError());
return 0;
}
PGNSI pGNSI;
pGNSI = (PGNSI)GetProcAddress(hmod,"RtlSetProcessPlaceholderCompatibilityMode");
if (pGNSI == NULL)
{
wprintf(L"GetProcAddress failed with %u\n", GetLastError());
return 0;
}
CHAR c = pGNSI(PHCM_EXPOSE_PLACEHOLDERS);
文档:
Compatibility with applications that use reparse points
The cloud files API implements the placeholder system using reparse
points. A common misconception about reparse points is that they are
the same as symbolic links. This misconception is occasionally
reflected in application implementations, and as a result, many
existing applications hit errors when encountering any reparse point.
To mitigate this compatibility issue, the cloud files API always hides
its reparse points from all applications except for sync engines and
processes whose main image resides under %systemroot%. Applications
that understand reparse points correctly can force the platform to
expose cloud files API reparse points using
RtlSetProcessPlaceholderCompatibilityMode or
RtlSetThreadProcessPlaceholderCompatibilityMode.
我正在使用下面的代码在我的应用程序中检索重分析点信息。这对符号链接和联结非常有用,但对于 OneDrive 文件夹及其所有子项,'Not a reparse point' 会失败。
using (SafeFileHandle srcHandle = NativeMethods.CreateFile(@"C:\Users\UserName\OneDrive",
0,
System.IO.FileShare.Read,
IntPtr.Zero,
System.IO.FileMode.Open,
NativeMethods.FileFlags.BackupSemantics | NativeMethods.FileFlags.OpenReparsePoint,
IntPtr.Zero))
{
if (!srcHandle.IsInvalid)
{
NativeMethods.REPARSE_DATA_BUFFER rdb = new NativeMethods.REPARSE_DATA_BUFFER();
IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(rdb) + sizeof(uint) + sizeof(ushort) + sizeof(ushort) + 0xFFFF);
var outBufferSize = Marshal.SizeOf(typeof(NativeMethods.REPARSE_DATA_BUFFER));
var outBuffer = Marshal.AllocHGlobal(outBufferSize);
// Determine if it's a symbolic link or a junction point
try
{
int bytesRet = 0;
if (NativeMethods.DeviceIoControl(srcHandle, NativeMethods.FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, outBuffer, outBufferSize, ref bytesRet, IntPtr.Zero) != 0)
{
rdb = (NativeMethods.REPARSE_DATA_BUFFER)Marshal.PtrToStructure(pMem, rdb.GetType());
...
}
else // Fails with ERROR_NOT_A_REPARSE_POINT** (0x1126) on OneDrive folder and all it's child items
{
log.LogError("FSCTL_GET_REPARSE_POINT error=" + Marshal.GetHRForLastWin32Error());
}
}
catch (Exception e1)
{
log.LogError("FSCTL_GET_REPARSE_POINT exception error=" + e1.Message + " -> GetLastWin32Error=" + Marshal.GetLastWin32Error().ToString());
}
finally
{
Marshal.FreeHGlobal(pMem);
}
}
}
原生声明:
[DllImport("kernel32.dll", EntryPoint = "CreateFile", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(string fileName, FileAccessAPI desiredAccess, FileShare shareMode, IntPtr secAttrib, FileMode createDisp, FileFlags flags, IntPtr template);
public const int FSCTL_GET_REPARSE_POINT = 0x000900A8;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern unsafe int DeviceIoControl(SafeFileHandle hFile,
int control,
IntPtr inbuffer,
int bufferSize,
IntPtr outBuffer,
int outBufferSize,
ref int bytesRet,
IntPtr overlapped);
public const uint RP_SYMBOLICLINK = 0xA000000C;
public const uint RP_JUNCTION = 0xA0000003;
public const uint RP_REPARSETAG_WCI = 0x80000018;
public const uint RP_REPARSETAG_APP = 0x8000001b;
public const uint RP_CLOUD = 0x9000001A;
public const uint RP_CLOUD_1 = 0x9000101A;
...
[StructLayout(LayoutKind.Sequential)]
public struct REPARSE_DATA_BUFFER
{
public uint ReparseTag;
public ushort ReparseDataLength;
public ushort Reserved;
public ushort SubstituteNameOffset;
public ushort SubstituteNameLength;
public ushort PrintNameOffset;
public ushort PrintNameLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xFFF0)]
public byte[] PathBuffer;
}
[Flags()]
public enum FileFlags : uint
{
...
OpenReparsePoint = 0x00200000,
BackupSemantics = 0x02000000,
}
以下命令可以成功检索 OneDrive 文件夹的重分析点信息。
fsutil reparsepoint 查询 C:\Users\UserName\OneDrive
如果能确定如何让这段代码发挥作用,那将非常有用。非常令人沮丧的是,已确认具有重新分析点的文件夹会收到一条错误消息,表明它们没有。
我也在 C++ 中尝试过,但得到了同样的错误。
使用一些相关的 API 对其进行了测试。如有问题,欢迎指出。
使用GetFileAttributes
得到的文件属性:
FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY
但没有属性:FILE_ATTRIBUTE_REPARSE_POINT
。而OneDrive中的普通文件只有属性:
FILE_ATTRIBUTE_ARCHIVE
因此,OneDrive 文件夹及其所有子项没有重新分析点 属性。
这里是测试用例:
#include <windows.h>
#include <iostream>
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;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;
int main()
{
DWORD attr = GetFileAttributes(TEXT("C:\Users\UserName\OneDrive"));
if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
printf("with Attributes: FILE_ATTRIBUTE_REPARSE_POINT\n");
else
printf("without FILE_ATTRIBUTE_REPARSE_POINT, Attributes = %x\n",attr);
HANDLE hFile = CreateFile(
TEXT("C:\Users\UserName\OneDrive"),
0,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS| FILE_FLAG_OPEN_REPARSE_POINT,
NULL
);
if (hFile != INVALID_HANDLE_VALUE)
{
REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)malloc(sizeof(REPARSE_DATA_BUFFER)+ sizeof(ULONG)+sizeof(USHORT)+0xffff);
if (rdb)
{
DWORD outBufferSize = sizeof(REPARSE_DATA_BUFFER) + sizeof(ULONG) + sizeof(USHORT) + 0xffff;
DWORD bytesRet = 0;
if (DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, rdb, outBufferSize, &bytesRet, NULL))
wprintf(L"DeviceIoControl succeed! printfname = %s\n", rdb->MountPointReparseBuffer.PathBuffer[rdb->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)]);
else
wprintf(L"error code = %d\n", GetLastError());
free(rdb);
rdb = NULL;
}
else
printf("malloc failed\n");
}
}
并且使用fsutil reparsepoint query "C:\Users\UserName\OneDrive"
,输出总是如下:
基本没有可用的信息。此外,关闭 "Files On Demand" 将删除重新分析点。
编辑:
不同于GetFileAttributes
,FindFirstFile
can get this FILE_ATTRIBUTE_REPARSE_POINT
attribute. According to the document Reparse Point Tags:
To retrieve the reparse point tag, use the
FindFirstFile
function. If thedwFileAttributes
member includes theFILE_ATTRIBUTE_REPARSE_POINT
attribute, then thedwReserved0
member specifies the reparse point.
更新:
经与相关工程师确认后,此问题由于云文件会隐藏重解析信息,下面是相同的文档 为了克服这个问题,他们有两种方法。
- 将执行文件放在%systemroot%下
- 在调用reparse相关之前先调用下面几行代码activity
typedef NTSYSAPI CHAR(*PGNSI)(CHAR Mode);
#define PHCM_EXPOSE_PLACEHOLDERS ((CHAR)2)
HMODULE hmod = LoadLibrary(L"ntdll.dll");
if (hmod == NULL)
{
wprintf(L"LoadLibrary failed with %u\n", GetLastError());
return 0;
}
PGNSI pGNSI;
pGNSI = (PGNSI)GetProcAddress(hmod,"RtlSetProcessPlaceholderCompatibilityMode");
if (pGNSI == NULL)
{
wprintf(L"GetProcAddress failed with %u\n", GetLastError());
return 0;
}
CHAR c = pGNSI(PHCM_EXPOSE_PLACEHOLDERS);
文档:
Compatibility with applications that use reparse points
The cloud files API implements the placeholder system using reparse points. A common misconception about reparse points is that they are the same as symbolic links. This misconception is occasionally reflected in application implementations, and as a result, many existing applications hit errors when encountering any reparse point.
To mitigate this compatibility issue, the cloud files API always hides its reparse points from all applications except for sync engines and processes whose main image resides under %systemroot%. Applications that understand reparse points correctly can force the platform to expose cloud files API reparse points using RtlSetProcessPlaceholderCompatibilityMode or RtlSetThreadProcessPlaceholderCompatibilityMode.