mirror of
				https://github.com/winfsp/winfsp.git
				synced 2025-10-30 19:48:38 -05:00 
			
		
		
		
	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:
		| @@ -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: | ||||||
|   | |||||||
| @@ -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); | ||||||
|  |  | ||||||
|  |                     if (!FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE)) | ||||||
|  |                     { | ||||||
|                         RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0); |                         RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0); | ||||||
|                         RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1); |                         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); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user