sys,dll: FspFileSystemResolveReparsePoints: use IO_REPARSE_TAG_SYMLINK instead of IO_REPARSE for symlink resolution

- FspFsvolCreateComplete STATUS_REPARSE handling changed to support device-relative symlink reparse points
This commit is contained in:
Bill Zissimopoulos 2016-09-14 22:16:40 -07:00
parent 46a29f663a
commit 5771eedc45
2 changed files with 63 additions and 31 deletions

View File

@ -1057,6 +1057,7 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent0, PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent0,
PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize)
{ {
PREPARSE_DATA_BUFFER OutputReparseData;
PWSTR TargetPath, RemainderPath, LastPathComponent, NewRemainderPath, ReparseTargetPath; PWSTR TargetPath, RemainderPath, LastPathComponent, NewRemainderPath, ReparseTargetPath;
WCHAR RemainderChar; WCHAR RemainderChar;
union union
@ -1070,10 +1071,17 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
ULONG MaxTries = 32; ULONG MaxTries = 32;
NTSTATUS Result; NTSTATUS Result;
TargetPath = Buffer;
RemainderPathSize = (lstrlenW(FileName) + 1) * sizeof(WCHAR); RemainderPathSize = (lstrlenW(FileName) + 1) * sizeof(WCHAR);
if (RemainderPathSize > *PSize) if (FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) +
RemainderPathSize > *PSize)
return STATUS_REPARSE_POINT_NOT_RESOLVED; return STATUS_REPARSE_POINT_NOT_RESOLVED;
OutputReparseData = Buffer;
memset(OutputReparseData, 0,
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer));
OutputReparseData->ReparseTag = IO_REPARSE_TAG_SYMLINK;
OutputReparseData->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
TargetPath = OutputReparseData->SymbolicLinkReparseBuffer.PathBuffer;
memcpy(TargetPath, FileName, RemainderPathSize); memcpy(TargetPath, FileName, RemainderPathSize);
ResolveLastPathComponent = ResolveLastPathComponent0; ResolveLastPathComponent = ResolveLastPathComponent0;
@ -1089,7 +1097,7 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
if (L'\0' == *RemainderPath) if (L'\0' == *RemainderPath)
{ {
if (!ResolveLastPathComponent) if (!ResolveLastPathComponent)
goto exit; goto symlink_exit;
ResolveLastPathComponent = FALSE; ResolveLastPathComponent = FALSE;
break; break;
} }
@ -1164,44 +1172,57 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
if (IO_REPARSE_TAG_SYMLINK != ReparseData->ReparseTag) if (IO_REPARSE_TAG_SYMLINK != ReparseData->ReparseTag)
goto reparse_data_exit; goto reparse_data_exit;
if (0 == --MaxTries)
return STATUS_REPARSE_POINT_NOT_RESOLVED;
ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
/* if an absolute (in the NT namespace) symlink return the full reparse point */
if (0 == (ReparseData->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) &&
ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0])
goto reparse_data_exit;
if (0 == --MaxTries)
return STATUS_REPARSE_POINT_NOT_RESOLVED;
/* if device relative symlink replace whole path; else replace last path component */ /* if device relative symlink replace whole path; else replace last path component */
NewRemainderPath = ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0] ? NewRemainderPath = ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0] ?
TargetPath : LastPathComponent; TargetPath : LastPathComponent;
reparse: reparse:
RemainderPathSize = (lstrlenW(RemainderPath) + 1) * sizeof(WCHAR); RemainderPathSize = (lstrlenW(RemainderPath) + 1) * sizeof(WCHAR);
if (NewRemainderPath + (ReparseTargetPathLength + RemainderPathSize) / sizeof(WCHAR) > if ((PUINT8)NewRemainderPath + ReparseTargetPathLength + RemainderPathSize >
TargetPath + *PSize / sizeof(WCHAR)) (PUINT8)Buffer + *PSize)
return STATUS_REPARSE_POINT_NOT_RESOLVED; return STATUS_REPARSE_POINT_NOT_RESOLVED;
/* move remainder path to its new position */ /* move remainder path to its new position */
memmove(NewRemainderPath + ReparseTargetPathLength / sizeof(WCHAR), memmove((PUINT8)NewRemainderPath + ReparseTargetPathLength,
RemainderPath, RemainderPathSize); RemainderPath, RemainderPathSize);
/* copy symlink target */ /* copy symlink target */
memcpy(NewRemainderPath, ReparseTargetPath, ReparseTargetPathLength); memcpy(NewRemainderPath, ReparseTargetPath, ReparseTargetPathLength);
/* if an absolute (in the NT namespace) symlink exit now */
if (0 != ReparseTargetPath /* ensure we are not doing dot handling */ &&
0 == (ReparseData->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) &&
ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0])
{
OutputReparseData->SymbolicLinkReparseBuffer.Flags = 0;
goto symlink_exit;
}
ResolveLastPathComponent = ResolveLastPathComponent0; ResolveLastPathComponent = ResolveLastPathComponent0;
RemainderPath = NewRemainderPath; RemainderPath = NewRemainderPath;
} }
exit: symlink_exit:
*PSize = (PUINT8)RemainderPath - (PUINT8)TargetPath; OutputReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength =
OutputReparseData->SymbolicLinkReparseBuffer.PrintNameLength =
(USHORT)lstrlenW(OutputReparseData->SymbolicLinkReparseBuffer.PathBuffer) * sizeof(WCHAR);
OutputReparseData->ReparseDataLength =
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) -
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer) +
OutputReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
*PSize = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer) +
OutputReparseData->ReparseDataLength;
PIoStatus->Status = STATUS_REPARSE; PIoStatus->Status = STATUS_REPARSE;
PIoStatus->Information = 0/*IO_REPARSE*/; PIoStatus->Information = ReparseData->ReparseTag;
return STATUS_REPARSE; return STATUS_REPARSE;
reparse_data_exit: reparse_data_exit:

View File

@ -510,22 +510,24 @@ NTSTATUS FspFsvolCreateComplete(
IO_REPARSE_TAG_SYMLINK == Response->IoStatus.Information) IO_REPARSE_TAG_SYMLINK == Response->IoStatus.Information)
{ {
/* /*
* IO_REPARSE means that the user-mode file system has returned a device-relative * IO_REPARSE means that the user-mode file system has returned an absolute (in
* (absolute in the device namespace) path. Prefix it with our device name/prefix * the device namespace) path. Send it as is to the IO Manager.
* and send it to the IO Manager.
* *
* IO_REPARSE_TAG_SYMLINK means that the user-mode file system has returned a full * IO_REPARSE_TAG_SYMLINK means that the user-mode file system has returned a full
* symbolic link reparse buffer with an absolute (in the NT namespace) path. Send * symbolic link reparse buffer. If the symbolic link is absolute send it to the
* it as is to the IO Manager. The FSD cannot handle relative symbolic links, so * IO Manager as is. If the symbolic link is device-relative (absolute in the
* it is the responsibility of the user-mode file system to resolve them. * device namespace) prefix it with our volume name/prefix and then send it to the
* IO Manager.
*
* We do our own handling of IO_REPARSE_TAG_SYMLINK reparse points because
* experimentation with handling them directly to the IO Manager revealed problems
* with UNC paths (\??\UNC\{VolumePrefix}\{FilePath}).
*/ */
if (IO_REPARSE == Response->IoStatus.Information) if (IO_REPARSE == Response->IoStatus.Information)
{ {
RtlCopyMemory(&ReparseTargetPrefix0, &FsvolDeviceExtension->VolumeName, RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0);
sizeof ReparseTargetPrefix0); RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1);
RtlCopyMemory(&ReparseTargetPrefix1, &FsvolDeviceExtension->VolumePrefix,
sizeof ReparseTargetPrefix1);
ReparseTargetPath.Length = ReparseTargetPath.MaximumLength = ReparseTargetPath.Length = ReparseTargetPath.MaximumLength =
Response->Rsp.Create.Reparse.Buffer.Size; Response->Rsp.Create.Reparse.Buffer.Size;
@ -552,16 +554,25 @@ NTSTATUS FspFsvolCreateComplete(
if (!NT_SUCCESS(Result)) if (!NT_SUCCESS(Result))
FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED);
RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0); if (!FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE))
RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1); {
RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0);
RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1);
}
else
{
RtlCopyMemory(&ReparseTargetPrefix0, &FsvolDeviceExtension->VolumeName,
sizeof ReparseTargetPrefix0);
RtlCopyMemory(&ReparseTargetPrefix1, &FsvolDeviceExtension->VolumePrefix,
sizeof ReparseTargetPrefix1);
}
ReparseTargetPath.Buffer = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseTargetPath.Buffer = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
ReparseTargetPath.Length = ReparseTargetPath.MaximumLength = ReparseTargetPath.Length = ReparseTargetPath.MaximumLength =
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
if (FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE) || if (ReparseTargetPath.Length < sizeof(WCHAR) || L'\\' != ReparseTargetPath.Buffer[0])
ReparseTargetPath.Length < sizeof(WCHAR) || L'\\' != ReparseTargetPath.Buffer[0])
FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED);
} }