From bd0acf22899db612c0fb645d0f084d89dae1c302 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sun, 11 Sep 2016 17:04:01 -0700 Subject: [PATCH] sys,dll: symbolic link testing --- src/dll/fsop.c | 92 ++++++++++++++++++--------------- src/sys/create.c | 38 ++++++++------ src/sys/fsctl.c | 2 +- tst/winfsp-tests/reparse-test.c | 37 +++++++++---- 4 files changed, 98 insertions(+), 71 deletions(-) diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 6b136375..83de24e7 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1056,87 +1056,87 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize), PVOID Context, - PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent0, PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) { - WCHAR c, *p, *lastp; - PWSTR TargetPath, TargetPathEnd, ReparseTargetPath; + PWSTR TargetPath, RemainderPath, LastPathComponent, NewRemainderPath, ReparseTargetPath; + WCHAR RemainderChar; union { REPARSE_DATA_BUFFER V; UINT8 B[FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX]; } ReparseDataBuf; PREPARSE_DATA_BUFFER ReparseData = &ReparseDataBuf.V; - SIZE_T Size, ReparseTargetPathLength, MaxTries = 32; + SIZE_T ReparseDataSize, RemainderPathSize, ReparseTargetPathLength; + BOOLEAN ResolveLastPathComponent; + ULONG MaxTries = 32; NTSTATUS Result; TargetPath = Buffer; - TargetPathEnd = TargetPath + *PSize / sizeof(WCHAR); - Size = (lstrlenW(FileName) + 1) * sizeof(WCHAR); - if (Size > *PSize) + RemainderPathSize = (lstrlenW(FileName) + 1) * sizeof(WCHAR); + if (RemainderPathSize > *PSize) return STATUS_REPARSE_POINT_NOT_RESOLVED; - memcpy(TargetPath, FileName, Size); + memcpy(TargetPath, FileName, RemainderPathSize); - p = TargetPath + ReparsePointIndex; - c = *p; + ResolveLastPathComponent = ResolveLastPathComponent0; + RemainderPath = TargetPath + ReparsePointIndex; for (;;) { - while (L'\\' == *p) - p++; - lastp = p; - while (L'\\' != *p) + while (L'\\' == *RemainderPath) + RemainderPath++; + LastPathComponent = RemainderPath; + while (L'\\' != *RemainderPath) { - if (L'\0' == *p) + if (L'\0' == *RemainderPath) { if (!ResolveLastPathComponent) goto exit; ResolveLastPathComponent = FALSE; break; } - p++; + RemainderPath++; } /* handle dot and dotdot! */ - if (L'.' == lastp[0]) + if (L'.' == LastPathComponent[0]) { - if (p == lastp + 1) + if (RemainderPath == LastPathComponent + 1) /* dot */ continue; - if (L'.' == lastp[1] && p == lastp + 2) + if (L'.' == LastPathComponent[1] && RemainderPath == LastPathComponent + 2) { /* dotdot */ - p = lastp; - while (TargetPath < p) + RemainderPath = LastPathComponent; + while (TargetPath < RemainderPath) { - p--; - if (L'\\' != *p) + RemainderPath--; + if (L'\\' != *RemainderPath) break; } - while (TargetPath < p) + while (TargetPath < RemainderPath) { - p--; - if (L'\\' == *p) + RemainderPath--; + if (L'\\' == *RemainderPath) break; } continue; } } - c = *p; - *p = L'\0'; - Size = sizeof ReparseDataBuf; - Result = GetReparsePointByName(FileSystem, Context, TargetPath, '\0' != c, - ReparseData, &Size); - *p = c; + RemainderChar = *RemainderPath; *RemainderPath = L'\0'; + ReparseDataSize = sizeof ReparseDataBuf; + Result = GetReparsePointByName(FileSystem, Context, TargetPath, '\0' != RemainderChar, + ReparseData, &ReparseDataSize); + *RemainderPath = RemainderChar; if (STATUS_NOT_A_REPARSE_POINT == Result) /* it was not a reparse point; continue */ continue; else if (!NT_SUCCESS(Result)) { - if (STATUS_OBJECT_NAME_NOT_FOUND != Result || '\0' != c) + if (STATUS_OBJECT_NAME_NOT_FOUND != Result || '\0' != RemainderChar) Result = STATUS_OBJECT_PATH_NOT_FOUND; return Result; } @@ -1162,30 +1162,38 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, return STATUS_REPARSE_POINT_NOT_RESOLVED; /* if device relative symlink replace whole path; else replace last path component */ - p = ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0] ? - TargetPath : lastp; + NewRemainderPath = ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0] ? + TargetPath : LastPathComponent; - if (ReparseTargetPathLength + sizeof(WCHAR) > (TargetPathEnd - p) * sizeof(WCHAR)) + RemainderPathSize = (lstrlenW(RemainderPath) + 1) * sizeof(WCHAR); + if (NewRemainderPath + (ReparseTargetPathLength + RemainderPathSize) / sizeof(WCHAR) > + TargetPath + *PSize / sizeof(WCHAR)) return STATUS_REPARSE_POINT_NOT_RESOLVED; - memcpy(p, ReparseTargetPath, ReparseTargetPathLength); - p[ReparseTargetPathLength / sizeof(WCHAR)] = L'\0'; + /* move remainder path to its new position */ + memmove(NewRemainderPath + ReparseTargetPathLength, RemainderPath, RemainderPathSize); + + /* copy symlink target */ + memcpy(NewRemainderPath, ReparseTargetPath, ReparseTargetPathLength); + + ResolveLastPathComponent = ResolveLastPathComponent0; + RemainderPath = NewRemainderPath; } exit: - *PSize = (PUINT8)p - (PUINT8)TargetPath; + *PSize = (PUINT8)RemainderPath - (PUINT8)TargetPath; PIoStatus->Status = STATUS_REPARSE; PIoStatus->Information = 0/*IO_REPARSE*/; return STATUS_REPARSE; reparse_data_exit: - if (Size > *PSize) + if (ReparseDataSize > *PSize) return IO_REPARSE_TAG_SYMLINK != ReparseData->ReparseTag ? STATUS_IO_REPARSE_DATA_INVALID : STATUS_REPARSE_POINT_NOT_RESOLVED; - *PSize = Size; - memcpy(Buffer, ReparseData, Size); + *PSize = ReparseDataSize; + memcpy(Buffer, ReparseData, ReparseDataSize); PIoStatus->Status = STATUS_REPARSE; PIoStatus->Information = ReparseData->ReparseTag; diff --git a/src/sys/create.c b/src/sys/create.c index 647b0ce4..6968ea4c 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -485,7 +485,7 @@ NTSTATUS FspFsvolCreateComplete( FSP_FILE_NODE *FileNode = FileDesc->FileNode; FSP_FILE_NODE *OpenedFileNode; PREPARSE_DATA_BUFFER ReparseData; - UNICODE_STRING ReparseTargetPrefix, ReparseTargetPath; + UNICODE_STRING ReparseTargetPrefix0, ReparseTargetPrefix1, ReparseTargetPath; if (FspFsctlTransactCreateKind == Request->Kind) { @@ -514,14 +514,15 @@ NTSTATUS FspFsvolCreateComplete( * path. Prefix it with our device name and send it to the IO Manager. * * IO_REPARSE_TAG_SYMLINK means that the user-mode file system has returned a full - * symbolic link reparse buffer. In this case send the target path to the IO Manager, - * and prefix it with our device name depending on SYMLINK_FLAG_RELATIVE. + * symbolic link reparse buffer. In this case send the target path to the IO Manager. */ if (IO_REPARSE == Response->IoStatus.Information) { - RtlCopyMemory(&ReparseTargetPrefix, &FsvolDeviceExtension->VolumeName, - sizeof ReparseTargetPrefix); + RtlCopyMemory(&ReparseTargetPrefix0, &FsvolDeviceExtension->VolumeName, + sizeof ReparseTargetPrefix0); + RtlCopyMemory(&ReparseTargetPrefix1, &FsvolDeviceExtension->VolumePrefix, + sizeof ReparseTargetPrefix1); ReparseTargetPath.Length = ReparseTargetPath.MaximumLength = Response->Rsp.Create.Reparse.FileName.Size; @@ -529,7 +530,9 @@ NTSTATUS FspFsvolCreateComplete( (PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.FileName.Offset); if ((PUINT8)ReparseTargetPath.Buffer + ReparseTargetPath.Length > - (PUINT8)Response + Response->Size || 0 == ReparseTargetPath.Length) + (PUINT8)Response + Response->Size || + sizeof(WCHAR) > ReparseTargetPath.Length || + L'\\' != ReparseTargetPath.Buffer[0]) FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); } else @@ -540,18 +543,18 @@ NTSTATUS FspFsvolCreateComplete( if ((PUINT8)ReparseData + Response->Rsp.Create.Reparse.Data.Size > (PUINT8)Response + Response->Size) - FSP_RETURN(Result = STATUS_IO_REPARSE_DATA_INVALID); + FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); Result = FsRtlValidateReparsePointBuffer(Response->Rsp.Create.Reparse.Data.Size, ReparseData); if (!NT_SUCCESS(Result)) - FSP_RETURN(); + FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); - if (0 == (ReparseData->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE)) - RtlZeroMemory(&ReparseTargetPrefix, sizeof ReparseTargetPrefix); - else - RtlCopyMemory(&ReparseTargetPrefix, &FsvolDeviceExtension->VolumeName, - sizeof ReparseTargetPrefix); + if (FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE)) + FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); + + RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0); + RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1); ReparseTargetPath.Buffer = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); @@ -559,20 +562,21 @@ NTSTATUS FspFsvolCreateComplete( ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; } - if (ReparseTargetPrefix.Length + ReparseTargetPath.Length > + if (ReparseTargetPrefix0.Length + ReparseTargetPrefix1.Length + ReparseTargetPath.Length > FileObject->FileName.MaximumLength) { PVOID Buffer = FspAllocExternal( - ReparseTargetPrefix.Length + ReparseTargetPath.Length); + ReparseTargetPrefix0.Length + ReparseTargetPrefix1.Length + ReparseTargetPath.Length); if (0 == Buffer) FSP_RETURN(Result = STATUS_INSUFFICIENT_RESOURCES); FspFreeExternal(FileObject->FileName.Buffer); FileObject->FileName.MaximumLength = - ReparseTargetPrefix.Length + ReparseTargetPath.Length; + ReparseTargetPrefix0.Length + ReparseTargetPrefix1.Length + ReparseTargetPath.Length; FileObject->FileName.Buffer = Buffer; } FileObject->FileName.Length = 0; - RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPrefix); + RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPrefix0); + RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPrefix1); RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPath); /* diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c index aa35dbfa..fdebebde 100644 --- a/src/sys/fsctl.c +++ b/src/sys/fsctl.c @@ -145,7 +145,7 @@ static NTSTATUS FspFsvolFileSystemControlReparsePoint( ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; /* is this an absolute path? determine if target resides on same device as link */ - if (0 == (ReparseData->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) && + if (!FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE) && ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0]) { UNICODE_STRING TargetObjectName; diff --git a/tst/winfsp-tests/reparse-test.c b/tst/winfsp-tests/reparse-test.c index 58eaceb1..c405d554 100644 --- a/tst/winfsp-tests/reparse-test.c +++ b/tst/winfsp-tests/reparse-test.c @@ -219,23 +219,15 @@ void reparse_nfs_test(void) } } -static void reparse_symlink_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) +static void reparse_symlink_dotest0(ULONG Flags, PWSTR Prefix, + PWSTR FilePath, PWSTR LinkPath, PWSTR TargetPath) { - void *memfs = memfs_start_ex(Flags, FileInfoTimeout); - HANDLE Handle; BOOL Success; - WCHAR FilePath[MAX_PATH], LinkPath[MAX_PATH]; PUINT8 NameInfoBuf[sizeof(FILE_NAME_INFO) + MAX_PATH]; PFILE_NAME_INFO PNameInfo = (PVOID)NameInfoBuf; - StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0", - Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); - - StringCbPrintfW(LinkPath, sizeof LinkPath, L"%s%s\\link0", - Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); - - Success = CreateSymbolicLinkW(LinkPath, FilePath, 0); + Success = CreateSymbolicLinkW(LinkPath, TargetPath, 0); if (Success) { Handle = CreateFileW(FilePath, @@ -277,6 +269,29 @@ static void reparse_symlink_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTime ASSERT(ERROR_PRIVILEGE_NOT_HELD == GetLastError()); FspDebugLog(__FUNCTION__ ": need SE_CREATE_SYMBOLIC_LINK_PRIVILEGE\n"); } +} + +static void reparse_symlink_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) +{ + void *memfs = memfs_start_ex(Flags, FileInfoTimeout); + + WCHAR FilePath[MAX_PATH], LinkPath[MAX_PATH], TargetPath[MAX_PATH]; + PUINT8 NameInfoBuf[sizeof(FILE_NAME_INFO) + MAX_PATH]; + PFILE_NAME_INFO PNameInfo = (PVOID)NameInfoBuf; + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + StringCbPrintfW(LinkPath, sizeof LinkPath, L"%s%s\\link0", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + reparse_symlink_dotest0(Flags, Prefix, FilePath, LinkPath, FilePath); + + StringCbPrintfW(TargetPath, sizeof TargetPath, L"%s\\file0", + -1 == Flags ? Prefix + 6 : L""); + reparse_symlink_dotest0(Flags, Prefix, FilePath, LinkPath, TargetPath); + + reparse_symlink_dotest0(Flags, Prefix, FilePath, LinkPath, L"file0"); memfs_stop(memfs); }