From ce83619728dffeb2bd64d8d7060bdd3a1dfdad84 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 15 Apr 2019 15:04:31 -0700 Subject: [PATCH] sys: FileStatLxInformation and friends --- build/VStudio/testing/winfsp-tests.vcxproj | 1 + .../testing/winfsp-tests.vcxproj.filters | 3 + src/sys/driver.h | 23 ++ src/sys/fileinfo.c | 231 ++++++++++++++++++ src/sys/util.c | 64 ++++- tst/winfsp-tests/winfsp-tests.c | 1 + tst/winfsp-tests/wsl-test.c | 181 ++++++++++++++ 7 files changed, 497 insertions(+), 7 deletions(-) create mode 100644 tst/winfsp-tests/wsl-test.c diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj index d4f78a30..b2d67af9 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj +++ b/build/VStudio/testing/winfsp-tests.vcxproj @@ -208,6 +208,7 @@ + diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters index 26eb09a9..67ff3de6 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj.filters +++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters @@ -97,6 +97,9 @@ Source + + Source + diff --git a/src/sys/driver.h b/src/sys/driver.h index b7c704ed..9326a43c 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -494,6 +494,9 @@ NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK Desir PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject); NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length); +NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, + PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength, + PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength); NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); NTSTATUS FspLockUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress, ULONG ExtraPriorityFlags); @@ -1636,4 +1639,24 @@ LOGICAL RtlEqualMemory(const VOID *Source1, const VOID *Source2, SIZE_T Length) return Length == RtlCompareMemory(Source1, Source2, Length); } +typedef struct _FILE_STAT_LX_INFORMATION +{ + LARGE_INTEGER FileId; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; + ULONG ReparseTag; + ULONG NumberOfLinks; + ACCESS_MASK EffectiveAccess; + ULONG LxFlags; + ULONG LxUid; + ULONG LxGid; + ULONG LxMode; + ULONG LxDeviceIdMajor; + ULONG LxDeviceIdMinor; +} FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION; #endif diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index b17ca63f..690a0feb 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -45,6 +45,15 @@ static NTSTATUS FspFsvolQueryPositionInformation(PFILE_OBJECT FileObject, static NTSTATUS FspFsvolQueryStandardInformation(PFILE_OBJECT FileObject, PVOID *PBuffer, PVOID BufferEnd, const FSP_FSCTL_FILE_INFO *FileInfo); +static NTSTATUS FspFsvolQueryStatInformation(PFILE_OBJECT FileObject, + PVOID *PBuffer, PVOID BufferEnd, + const FSP_FSCTL_FILE_INFO *FileInfo); +static NTSTATUS FspFsvolQueryStatLxInformation(PFILE_OBJECT FileObject, + PVOID *PBuffer, PVOID BufferEnd, + const FSP_FSCTL_FILE_INFO *FileInfo); +static NTSTATUS FspFsvolQueryStatLxInformationEa( + PDEVICE_OBJECT FsvolDeviceObject, PFILE_OBJECT FileObject, + PVOID *PBuffer, PVOID BufferEnd); static NTSTATUS FspFsvolQueryStreamInformationCopy( FSP_FSCTL_STREAM_INFO *StreamInfoBuffer, ULONG StreamInfoBufferSize, PVOID DestBuf, PULONG PDestLen); @@ -97,6 +106,9 @@ FAST_IO_QUERY_OPEN FspFastIoQueryOpen; #pragma alloc_text(PAGE, FspFsvolQueryNetworkOpenInformation) #pragma alloc_text(PAGE, FspFsvolQueryPositionInformation) #pragma alloc_text(PAGE, FspFsvolQueryStandardInformation) +#pragma alloc_text(PAGE, FspFsvolQueryStatInformation) +#pragma alloc_text(PAGE, FspFsvolQueryStatLxInformation) +#pragma alloc_text(PAGE, FspFsvolQueryStatLxInformationEa) #pragma alloc_text(PAGE, FspFsvolQueryStreamInformationCopy) #pragma alloc_text(PAGE, FspFsvolQueryStreamInformation) #pragma alloc_text(PAGE, FspFsvolQueryStreamInformationSuccess) @@ -417,6 +429,195 @@ static NTSTATUS FspFsvolQueryStandardInformation(PFILE_OBJECT FileObject, return STATUS_SUCCESS; } +static NTSTATUS FspFsvolQueryStatInformation(PFILE_OBJECT FileObject, + PVOID *PBuffer, PVOID BufferEnd, + const FSP_FSCTL_FILE_INFO *FileInfo) +{ + PAGED_CODE(); + + PFILE_STAT_INFORMATION Info = (PFILE_STAT_INFORMATION)*PBuffer; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + + ASSERT(FileNode == FileDesc->FileNode); + + if (0 == FileInfo) + { + if ((PVOID)(Info + 1) > BufferEnd) + return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; + } + + Info->FileId.QuadPart = FileNode->IndexNumber; + Info->CreationTime.QuadPart = FileInfo->CreationTime; + Info->LastAccessTime.QuadPart = FileInfo->LastAccessTime; + Info->LastWriteTime.QuadPart = FileInfo->LastWriteTime; + Info->ChangeTime.QuadPart = FileInfo->ChangeTime; + Info->AllocationSize.QuadPart = FileInfo->AllocationSize; + Info->EndOfFile.QuadPart = FileInfo->FileSize; + Info->FileAttributes = 0 != FileInfo->FileAttributes ? + FileInfo->FileAttributes : FILE_ATTRIBUTE_NORMAL; + Info->ReparseTag = FileInfo->ReparseTag; + Info->NumberOfLinks = 1; + Info->EffectiveAccess = FileDesc->GrantedAccess; + + *PBuffer = (PVOID)(Info + 1); + + return STATUS_SUCCESS; +} + +static NTSTATUS FspFsvolQueryStatLxInformation(PFILE_OBJECT FileObject, + PVOID *PBuffer, PVOID BufferEnd, + const FSP_FSCTL_FILE_INFO *FileInfo) +{ + PAGED_CODE(); + + PFILE_STAT_LX_INFORMATION Info = (PFILE_STAT_LX_INFORMATION)*PBuffer; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + + ASSERT(FileNode == FileDesc->FileNode); + + if (0 == FileInfo) + { + if ((PVOID)(Info + 1) > BufferEnd) + return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; + } + + Info->FileId.QuadPart = FileNode->IndexNumber; + Info->CreationTime.QuadPart = FileInfo->CreationTime; + Info->LastAccessTime.QuadPart = FileInfo->LastAccessTime; + Info->LastWriteTime.QuadPart = FileInfo->LastWriteTime; + Info->ChangeTime.QuadPart = FileInfo->ChangeTime; + Info->AllocationSize.QuadPart = FileInfo->AllocationSize; + Info->EndOfFile.QuadPart = FileInfo->FileSize; + Info->FileAttributes = 0 != FileInfo->FileAttributes ? + FileInfo->FileAttributes : FILE_ATTRIBUTE_NORMAL; + Info->ReparseTag = FileInfo->ReparseTag; + Info->NumberOfLinks = 1; + Info->EffectiveAccess = FileDesc->GrantedAccess; + + *PBuffer = (PVOID)(Info + 1); + + return STATUS_SUCCESS; +} + +static NTSTATUS FspFsvolQueryStatLxInformationEa( + PDEVICE_OBJECT FsvolDeviceObject, PFILE_OBJECT FileObject, + PVOID *PBuffer, PVOID BufferEnd) +{ +#define ADD_GET_EA(Name, End) \ + GetEa->NextEntryOffset = (ULONG)(End ? 0 :\ + FSP_FSCTL_ALIGN_UP( \ + FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + sizeof("" Name),\ + sizeof(ULONG))); \ + GetEa->EaNameLength = (UCHAR)(sizeof("" Name) - 1);\ + RtlCopyMemory(GetEa->EaName, "" Name, sizeof("" Name));\ + GetEaLength = (ULONG)((PUINT8)GetEa - (PUINT8)&GetEaBuf.V +\ + FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + sizeof("" Name));\ + GetEa = (PVOID)((PUINT8)GetEa + GetEa->NextEntryOffset); +#define EQUAL_EA_NAME(Name) \ + CmpName.Length = \ + CmpName.MaximumLength = sizeof("" Name) - 1,\ + CmpName.Buffer = "" Name, \ + RtlEqualString(&CmpName, &EaName, TRUE/* always case-insensitive */) + + PAGED_CODE(); + + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + PFILE_STAT_LX_INFORMATION Info = (PFILE_STAT_LX_INFORMATION)*PBuffer; + union + { + FILE_GET_EA_INFORMATION V; + UINT8 B[256]; + } GetEaBuf; + PFILE_GET_EA_INFORMATION GetEa = &GetEaBuf.V; + ULONG GetEaLength = 0; + union + { + FILE_FULL_EA_INFORMATION V; + UINT8 B[256]; + } EaBuf; + ULONG EaLength = sizeof EaBuf; + STRING EaName, CmpName; + NTSTATUS Result; + + Info->LxFlags = 0; + Info->LxUid = 0; + Info->LxGid = 0; + Info->LxMode = 0; + Info->LxDeviceIdMajor = 0; + Info->LxDeviceIdMinor = 0; + + if (!FsvolDeviceExtension->VolumeParams.ExtendedAttributes) + return STATUS_SUCCESS; + + ADD_GET_EA("$LXUID", 0); + ADD_GET_EA("$LXGID", 0); + ADD_GET_EA("$LXMOD", 0); + ADD_GET_EA("$LXDEV", 1); + + Result = FspSendQueryEaIrp(FsvolDeviceObject/* bypass filters */, FileObject, + &GetEaBuf.V, GetEaLength, + &EaBuf.V, &EaLength); + if (!NT_SUCCESS(Result)) + return Result; + + for (PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V, EaEnd = (PVOID)((PUINT8)Ea + EaLength); + EaEnd > Ea; Ea = FSP_NEXT_EA(Ea, EaEnd)) + { + EaName.Length = + EaName.MaximumLength = Ea->EaNameLength, + EaName.Buffer = Ea->EaName; + + if (EQUAL_EA_NAME("$LXUID")) + { + if (sizeof(ULONG) == Ea->EaValueLength) + { + Info->LxFlags |= 0x1/*LX_FILE_METADATA_HAS_UID*/; + Info->LxUid = *(PULONG)(Ea->EaName + sizeof "$LXUID"); + } + } + else + if (EQUAL_EA_NAME("$LXGID")) + { + if (sizeof(ULONG) == Ea->EaValueLength) + { + Info->LxFlags |= 0x2/*LX_FILE_METADATA_HAS_GID*/; + Info->LxGid = *(PULONG)(Ea->EaName + sizeof "$LXGID"); + } + } + else + if (EQUAL_EA_NAME("$LXMOD")) + { + if (sizeof(ULONG) == Ea->EaValueLength) + { + Info->LxFlags |= 0x4/*LX_FILE_METADATA_HAS_MODE*/; + Info->LxMode = *(PULONG)(Ea->EaName + sizeof "$LXMOD"); + } + } + else + if (EQUAL_EA_NAME("$LXDEV")) + { + if (sizeof(UINT64) == Ea->EaValueLength) + { + Info->LxFlags |= 0x8/*LX_FILE_METADATA_HAS_DEVICE_ID*/; + UINT64 Dev = *(PUINT64)(Ea->EaName + sizeof "$LXDEV"); + Info->LxDeviceIdMajor = (Dev >> 32) & 0xffffffff; + Info->LxDeviceIdMinor = Dev & 0xffffffff; + } + } + } + + return STATUS_SUCCESS; + +#undef EQUAL_EA_NAME +#undef ADD_GET_EA +} + static NTSTATUS FspFsvolQueryStreamInformationCopy( FSP_FSCTL_STREAM_INFO *StreamInfo, ULONG StreamInfoSize, PVOID DestBuf, PULONG PDestLen) @@ -720,6 +921,12 @@ static NTSTATUS FspFsvolQueryInformation( case FileStandardInformation: Result = FspFsvolQueryStandardInformation(FileObject, &Buffer, BufferEnd, 0); break; + case FileStatInformation: + Result = FspFsvolQueryStatInformation(FileObject, &Buffer, BufferEnd, 0); + break; + case 70/*FileStatLxInformation*/: + Result = FspFsvolQueryStatLxInformation(FileObject, &Buffer, BufferEnd, 0); + break; default: Result = STATUS_INVALID_PARAMETER; return Result; @@ -729,6 +936,18 @@ static NTSTATUS FspFsvolQueryInformation( return Result; FspFileNodeAcquireShared(FileNode, Main); + + if (70/*FileStatLxInformation*/ == FileInformationClass) + { + Result = FspFsvolQueryStatLxInformationEa( + FsvolDeviceObject, FileObject, &Buffer, BufferEnd); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Main); + return Result; + } + } + if (FspFileNodeTryGetFileInfo(FileNode, &FileInfoBuf)) { FspFileNodeRelease(FileNode, Main); @@ -755,6 +974,12 @@ static NTSTATUS FspFsvolQueryInformation( case FileStandardInformation: Result = FspFsvolQueryStandardInformation(FileObject, &Buffer, BufferEnd, &FileInfoBuf); break; + case FileStatInformation: + Result = FspFsvolQueryStatInformation(FileObject, &Buffer, BufferEnd, &FileInfoBuf); + break; + case 70/*FileStatLxInformation*/: + Result = FspFsvolQueryStatLxInformation(FileObject, &Buffer, BufferEnd, &FileInfoBuf); + break; default: ASSERT(0); Result = STATUS_INVALID_PARAMETER; @@ -865,6 +1090,12 @@ NTSTATUS FspFsvolQueryInformationComplete( case FileStandardInformation: Result = FspFsvolQueryStandardInformation(FileObject, &Buffer, BufferEnd, FileInfo); break; + case FileStatInformation: + Result = FspFsvolQueryStatInformation(FileObject, &Buffer, BufferEnd, FileInfo); + break; + case 70/*FileStatLxInformation*/: + Result = FspFsvolQueryStatLxInformation(FileObject, &Buffer, BufferEnd, FileInfo); + break; default: ASSERT(0); Result = STATUS_INVALID_PARAMETER; diff --git a/src/sys/util.c b/src/sys/util.c index 52741ff8..58d84127 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -26,7 +26,10 @@ NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK Desir PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject); NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length); -static NTSTATUS FspSendSetInformationIrpCompletion( +NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, + PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength, + PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength); +static NTSTATUS FspSendIrpCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0); NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); NTSTATUS FspLockUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); @@ -124,6 +127,7 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); #pragma alloc_text(PAGE, FspCreateGuid) #pragma alloc_text(PAGE, FspGetDeviceObjectPointer) #pragma alloc_text(PAGE, FspSendSetInformationIrp) +#pragma alloc_text(PAGE, FspSendQueryEaIrp) #pragma alloc_text(PAGE, FspBufferUserBuffer) #pragma alloc_text(PAGE, FspLockUserBuffer) #pragma alloc_text(PAGE, FspMapLockedPagesInUserMode) @@ -275,7 +279,7 @@ typedef struct { IO_STATUS_BLOCK IoStatus; KEVENT Event; -} FSP_SEND_SET_INFORMATION_IRP_CONTEXT; +} FSP_SEND_IRP_CONTEXT; NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length) @@ -289,7 +293,7 @@ NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT File NTSTATUS Result; PIRP Irp; PIO_STACK_LOCATION IrpSp; - FSP_SEND_SET_INFORMATION_IRP_CONTEXT Context; + FSP_SEND_IRP_CONTEXT Context; if (0 == DeviceObject) DeviceObject = IoGetRelatedDeviceObject(FileObject); @@ -304,9 +308,9 @@ NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT File IrpSp->MajorFunction = IRP_MJ_SET_INFORMATION; IrpSp->FileObject = FileObject; IrpSp->Parameters.SetFile.FileInformationClass = FileInformationClass; - IrpSp->Parameters.SetFile.Length = FileInformationClass; + IrpSp->Parameters.SetFile.Length = Length; - IoSetCompletionRoutine(Irp, FspSendSetInformationIrpCompletion, &Context, TRUE, TRUE, TRUE); + IoSetCompletionRoutine(Irp, FspSendIrpCompletion, &Context, TRUE, TRUE, TRUE); KeInitializeEvent(&Context.Event, NotificationEvent, FALSE); Result = IoCallDriver(DeviceObject, Irp); @@ -316,12 +320,58 @@ NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT File return NT_SUCCESS(Result) ? Context.IoStatus.Status : Result; } -static NTSTATUS FspSendSetInformationIrpCompletion( +NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, + PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength, + PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength) +{ + PAGED_CODE(); + + NTSTATUS Result; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + FSP_SEND_IRP_CONTEXT Context; + ULONG EaLength = *PEaLength; + + *PEaLength = 0; + + if (0 == DeviceObject) + DeviceObject = IoGetRelatedDeviceObject(FileObject); + + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (0 == Irp) + return STATUS_INSUFFICIENT_RESOURCES; + + IrpSp = IoGetNextIrpStackLocation(Irp); + Irp->RequestorMode = KernelMode; + Irp->AssociatedIrp.SystemBuffer = Ea; + IrpSp->MajorFunction = IRP_MJ_QUERY_EA; + IrpSp->FileObject = FileObject; + IrpSp->Parameters.QueryEa.Length = EaLength; + IrpSp->Parameters.QueryEa.EaList = GetEa; + IrpSp->Parameters.QueryEa.EaListLength = GetEaLength; + + IoSetCompletionRoutine(Irp, FspSendIrpCompletion, &Context, TRUE, TRUE, TRUE); + + KeInitializeEvent(&Context.Event, NotificationEvent, FALSE); + Result = IoCallDriver(DeviceObject, Irp); + if (STATUS_PENDING == Result) + KeWaitForSingleObject(&Context.Event, Executive, KernelMode, FALSE, 0); + + if (!NT_SUCCESS(Result)) + return Result; + + if (NT_SUCCESS(Context.IoStatus.Status)) + *PEaLength = (ULONG)Context.IoStatus.Information; + + return Context.IoStatus.Status; +} + +static NTSTATUS FspSendIrpCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0) { // !PAGED_CODE(); - FSP_SEND_SET_INFORMATION_IRP_CONTEXT *Context = Context0; + FSP_SEND_IRP_CONTEXT *Context = Context0; Context->IoStatus = Irp->IoStatus; KeSetEvent(&Context->Event, 1, FALSE); diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c index e8be476c..57480565 100644 --- a/tst/winfsp-tests/winfsp-tests.c +++ b/tst/winfsp-tests/winfsp-tests.c @@ -209,6 +209,7 @@ int main(int argc, char *argv[]) TESTSUITE(ea_tests); TESTSUITE(stream_tests); TESTSUITE(oplock_tests); + TESTSUITE(wsl_tests); atexit(exiting); signal(SIGABRT, abort_handler); diff --git a/tst/winfsp-tests/wsl-test.c b/tst/winfsp-tests/wsl-test.c new file mode 100644 index 00000000..f590adfb --- /dev/null +++ b/tst/winfsp-tests/wsl-test.c @@ -0,0 +1,181 @@ +/** + * @file wsl-test.c + * + * @copyright 2015-2019 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include +#include +#include "memfs.h" + +#include "winfsp-tests.h" + +typedef struct _FILE_STAT_INFORMATION +{ + LARGE_INTEGER FileId; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; + ULONG ReparseTag; + ULONG NumberOfLinks; + ACCESS_MASK EffectiveAccess; +} FILE_STAT_INFORMATION, *PFILE_STAT_INFORMATION; + +typedef struct _FILE_STAT_LX_INFORMATION +{ + LARGE_INTEGER FileId; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; + ULONG ReparseTag; + ULONG NumberOfLinks; + ACCESS_MASK EffectiveAccess; + ULONG LxFlags; + ULONG LxUid; + ULONG LxGid; + ULONG LxMode; + ULONG LxDeviceIdMajor; + ULONG LxDeviceIdMinor; +} FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION; + +NTSTATUS NTAPI NtQueryInformationFile( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +static void wsl_stat_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) +{ + void *memfs = memfs_start_ex(Flags, FileInfoTimeout); + + HANDLE Handle; + WCHAR FilePath[MAX_PATH]; + FILE_STAT_INFORMATION StatInfo; + FILE_STAT_LX_INFORMATION StatLxInfo; + FILETIME FileTime; + LONGLONG TimeLo, TimeHi; + IO_STATUS_BLOCK IoStatus; + NTSTATUS Result; + + GetSystemTimeAsFileTime(&FileTime); + TimeLo = ((PLARGE_INTEGER)&FileTime)->QuadPart; + TimeHi = TimeLo + 10000 * 10000/* 10 seconds */; + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Handle = CreateFileW(FilePath, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0); + ASSERT(INVALID_HANDLE_VALUE != Handle); + + Result = NtQueryInformationFile(Handle, &IoStatus, &StatInfo, sizeof StatInfo, + 68/*FileStatInformation*/); + if (STATUS_SUCCESS == Result) + { + ASSERT(STATUS_SUCCESS == Result); + if (-1 != Flags) + ASSERT( + TimeLo <= StatInfo.CreationTime.QuadPart && + TimeHi > StatInfo.CreationTime.QuadPart); + ASSERT( + TimeLo <= StatInfo.LastAccessTime.QuadPart && + TimeHi > StatInfo.LastAccessTime.QuadPart); + ASSERT( + TimeLo <= StatInfo.LastWriteTime.QuadPart && + TimeHi > StatInfo.LastWriteTime.QuadPart); + ASSERT( + TimeLo <= StatInfo.ChangeTime.QuadPart && + TimeHi > StatInfo.ChangeTime.QuadPart); + ASSERT(0 == StatInfo.AllocationSize.QuadPart); + ASSERT(0 == StatInfo.EndOfFile.QuadPart); + //ASSERT(FILE_ATTRIBUTE_ARCHIVE == StatInfo.FileAttributes); + ASSERT(0 == StatInfo.ReparseTag); + ASSERT(1 == StatInfo.NumberOfLinks); + //tlib_printf("%lx %lx", FILE_GENERIC_READ | FILE_GENERIC_WRITE, StatInfo.EffectiveAccess); + //ASSERT((FILE_GENERIC_READ | FILE_GENERIC_WRITE) == StatInfo.EffectiveAccess); + + Result = NtQueryInformationFile(Handle, &IoStatus, &StatLxInfo, sizeof StatLxInfo, + 70/*FileStatLxInformation*/); + ASSERT(STATUS_SUCCESS == Result); + if (-1 != Flags) + ASSERT( + TimeLo <= StatLxInfo.CreationTime.QuadPart && + TimeHi > StatLxInfo.CreationTime.QuadPart); + ASSERT( + TimeLo <= StatLxInfo.LastAccessTime.QuadPart && + TimeHi > StatLxInfo.LastAccessTime.QuadPart); + ASSERT( + TimeLo <= StatLxInfo.LastWriteTime.QuadPart && + TimeHi > StatLxInfo.LastWriteTime.QuadPart); + ASSERT( + TimeLo <= StatLxInfo.ChangeTime.QuadPart && + TimeHi > StatLxInfo.ChangeTime.QuadPart); + ASSERT(0 == StatLxInfo.AllocationSize.QuadPart); + ASSERT(0 == StatLxInfo.EndOfFile.QuadPart); + //ASSERT(FILE_ATTRIBUTE_ARCHIVE == StatLxInfo.FileAttributes); + ASSERT(0 == StatLxInfo.ReparseTag); + ASSERT(1 == StatLxInfo.NumberOfLinks); + //tlib_printf("%lx %lx", FILE_GENERIC_READ | FILE_GENERIC_WRITE, StatLxInfo.EffectiveAccess); + //ASSERT((FILE_GENERIC_READ | FILE_GENERIC_WRITE) == StatLxInfo.EffectiveAccess); + } + else + { + ASSERT(STATUS_INVALID_INFO_CLASS == Result); + FspDebugLog(__FUNCTION__ ": only works in Win10 with WSLinux\n"); + } + + CloseHandle(Handle); + + memfs_stop(memfs); +} + +static void wsl_stat_test(void) +{ + if (NtfsTests) + { + WCHAR DirBuf[MAX_PATH]; + GetTestDirectory(DirBuf); + wsl_stat_dotest(-1, DirBuf, 0); + } + if (WinFspDiskTests) + { + wsl_stat_dotest(MemfsDisk, 0, 0); + wsl_stat_dotest(MemfsDisk, 0, 1000); + } + if (WinFspNetTests) + { + wsl_stat_dotest(MemfsNet, L"\\\\memfs\\share", 0); + wsl_stat_dotest(MemfsNet, L"\\\\memfs\\share", 1000); + } +} + +void wsl_tests(void) +{ + TEST_OPT(wsl_stat_test); +}