From 5771eedc45906597940c9e8cb9ce05dc0107e5bf Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 14 Sep 2016 22:16:40 -0700 Subject: [PATCH] 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 --- src/dll/fsop.c | 55 +++++++++++++++++++++++++++++++++--------------- src/sys/create.c | 39 ++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 6a923a7b..4ec6d7f3 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1057,6 +1057,7 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent0, PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) { + PREPARSE_DATA_BUFFER OutputReparseData; PWSTR TargetPath, RemainderPath, LastPathComponent, NewRemainderPath, ReparseTargetPath; WCHAR RemainderChar; union @@ -1070,10 +1071,17 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, ULONG MaxTries = 32; NTSTATUS Result; - TargetPath = Buffer; 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; + + 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); ResolveLastPathComponent = ResolveLastPathComponent0; @@ -1089,7 +1097,7 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, if (L'\0' == *RemainderPath) { if (!ResolveLastPathComponent) - goto exit; + goto symlink_exit; ResolveLastPathComponent = FALSE; break; } @@ -1164,44 +1172,57 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, if (IO_REPARSE_TAG_SYMLINK != ReparseData->ReparseTag) goto reparse_data_exit; + if (0 == --MaxTries) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); 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 */ NewRemainderPath = ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0] ? TargetPath : LastPathComponent; reparse: RemainderPathSize = (lstrlenW(RemainderPath) + 1) * sizeof(WCHAR); - if (NewRemainderPath + (ReparseTargetPathLength + RemainderPathSize) / sizeof(WCHAR) > - TargetPath + *PSize / sizeof(WCHAR)) + if ((PUINT8)NewRemainderPath + ReparseTargetPathLength + RemainderPathSize > + (PUINT8)Buffer + *PSize) return STATUS_REPARSE_POINT_NOT_RESOLVED; /* move remainder path to its new position */ - memmove(NewRemainderPath + ReparseTargetPathLength / sizeof(WCHAR), + memmove((PUINT8)NewRemainderPath + ReparseTargetPathLength, RemainderPath, RemainderPathSize); /* copy symlink target */ 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; RemainderPath = NewRemainderPath; } -exit: - *PSize = (PUINT8)RemainderPath - (PUINT8)TargetPath; +symlink_exit: + 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->Information = 0/*IO_REPARSE*/; + PIoStatus->Information = ReparseData->ReparseTag; return STATUS_REPARSE; reparse_data_exit: diff --git a/src/sys/create.c b/src/sys/create.c index 30ad03ab..138c9665 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -510,22 +510,24 @@ NTSTATUS FspFsvolCreateComplete( IO_REPARSE_TAG_SYMLINK == Response->IoStatus.Information) { /* - * IO_REPARSE means that the user-mode file system has returned a device-relative - * (absolute in the device namespace) path. Prefix it with our device name/prefix - * and send it to the IO Manager. + * IO_REPARSE means that the user-mode file system has returned an absolute (in + * the device namespace) path. Send it as is to the IO Manager. * * 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 - * it as is to the IO Manager. The FSD cannot handle relative symbolic links, so - * it is the responsibility of the user-mode file system to resolve them. + * symbolic link reparse buffer. If the symbolic link is absolute send it to the + * IO Manager as is. If the symbolic link is device-relative (absolute in the + * 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) { - RtlCopyMemory(&ReparseTargetPrefix0, &FsvolDeviceExtension->VolumeName, - sizeof ReparseTargetPrefix0); - RtlCopyMemory(&ReparseTargetPrefix1, &FsvolDeviceExtension->VolumePrefix, - sizeof ReparseTargetPrefix1); + RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0); + RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1); ReparseTargetPath.Length = ReparseTargetPath.MaximumLength = Response->Rsp.Create.Reparse.Buffer.Size; @@ -552,16 +554,25 @@ NTSTATUS FspFsvolCreateComplete( if (!NT_SUCCESS(Result)) FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); - RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0); - RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1); + if (!FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE)) + { + 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 + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); ReparseTargetPath.Length = ReparseTargetPath.MaximumLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; - if (FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE) || - ReparseTargetPath.Length < sizeof(WCHAR) || L'\\' != ReparseTargetPath.Buffer[0]) + if (ReparseTargetPath.Length < sizeof(WCHAR) || L'\\' != ReparseTargetPath.Buffer[0]) FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); }