diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 4bf3c357..99d4e179 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -106,6 +106,7 @@ enum FspFsctlTransactLockControlKind, FspFsctlTransactQuerySecurityKind, FspFsctlTransactSetSecurityKind, + FspFsctlTransactQueryStreamInformationKind, FspFsctlTransactKindCount, }; enum @@ -142,7 +143,7 @@ typedef struct UINT32 PersistentAcls:1; /* file system preserves and enforces access control lists */ UINT32 ReparsePoints:1; /* file system supports reparse points */ UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */ - UINT32 NamedStreams:1; /* file system supports named streams (!!!: unimplemented) */ + UINT32 NamedStreams:1; /* file system supports named streams */ UINT32 HardLinks:1; /* unimplemented; set to 0 */ UINT32 ExtendedAttributes:1; /* unimplemented; set to 0 */ UINT32 ReadOnlyVolume:1; @@ -184,6 +185,13 @@ typedef struct WCHAR FileNameBuf[]; } FSP_FSCTL_DIR_INFO; typedef struct +{ + UINT16 Size; + UINT64 StreamSize; + UINT64 StreamAllocationSize; + WCHAR StreamNameBuf[]; +} FSP_FSCTL_STREAM_INFO; +typedef struct { UINT16 Offset; UINT16 Size; @@ -332,6 +340,11 @@ typedef struct UINT64 AccessToken; /* request access token (HANDLE) */ FSP_FSCTL_TRANSACT_BUF SecurityDescriptor; } SetSecurity; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + } QueryStreamInformation; } Req; FSP_FSCTL_TRANSACT_BUF FileName; /* {Create,Cleanup,SetInformation/{...},QueryDirectory} */ FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[]; @@ -401,6 +414,10 @@ typedef struct { FSP_FSCTL_TRANSACT_BUF SecurityDescriptor; /* Size==0 means no security descriptor returned */ } SetSecurity; + struct + { + FSP_FSCTL_TRANSACT_BUF Buffer; + } QueryStreamInformation; } Rsp; FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[]; } FSP_FSCTL_TRANSACT_RSP; diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 4f96282b..1f94591e 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -773,12 +773,36 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE FSP_FSCTL_TRANSACT_REQ *Request, PVOID FileNode, PWSTR FileName, PVOID Buffer, SIZE_T Size); + /** + * Get named streams information. + * + * @param FileSystem + * The file system on which this request is posted. + * @param Request + * The request posted by the kernel mode FSD. + * @param FileNode + * The file node of the file or directory to get stream information for. + * @param Buffer + * Pointer to a buffer that will receive the stream information. + * @param Length + * Length of buffer. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes stored. + * @return + * STATUS_SUCCESS or error code. + * @see + * FspFileSystemAddStreamInfo + */ + NTSTATUS (*GetStreamInfo)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode, PVOID Buffer, ULONG Length, + PULONG PBytesTransferred); /* * This ensures that this interface will always contain 64 function pointers. * Please update when changing the interface as it is important for future compatibility. */ - NTSTATUS (*Reserved[41])(); + NTSTATUS (*Reserved[40])(); } FSP_FILE_SYSTEM_INTERFACE; FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()), "FSP_FILE_SYSTEM_INTERFACE must have 64 entries."); @@ -1010,6 +1034,8 @@ FSP_API NTSTATUS FspFileSystemOpQuerySecurity(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_API NTSTATUS FspFileSystemOpSetSecurity(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpQueryStreamInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); /* * Helpers @@ -1150,6 +1176,30 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint( PVOID CurrentReparseData, SIZE_T CurrentReparseDataSize, PVOID ReplaceReparseData, SIZE_T ReplaceReparseDataSize); +/** + * Add named stream information to a buffer. + * + * This is a helper for implementing the GetStreamInfo operation. + * + * @param StreamInfo + * The stream information to add. A value of NULL acts as an EOF marker for a GetStreamInfo + * operation. + * @param Buffer + * Pointer to a buffer that will receive the stream information. This should contain + * the same value passed to the GetStreamInfo Buffer parameter. + * @param Length + * Length of buffer. This should contain the same value passed to the GetStreamInfo + * Length parameter. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes stored. This should + * contain the same value passed to the GetStreamInfo PBytesTransferred parameter. + * @return + * TRUE if the stream information was added, FALSE if there was not enough space to add it. + * @see + * GetStreamInfo + */ +FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred); /* * Security diff --git a/src/dll/fs.c b/src/dll/fs.c index 7ab3e8e6..b56fb6a3 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -99,6 +99,7 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath, FileSystem->Operations[FspFsctlTransactFileSystemControlKind] = FspFileSystemOpFileSystemControl; FileSystem->Operations[FspFsctlTransactQuerySecurityKind] = FspFileSystemOpQuerySecurity; FileSystem->Operations[FspFsctlTransactSetSecurityKind] = FspFileSystemOpSetSecurity; + FileSystem->Operations[FspFsctlTransactQueryStreamInformationKind] = FspFileSystemOpQueryStreamInformation; FileSystem->Interface = Interface; FileSystem->OpGuardStrategy = FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE; diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 3671684b..5bff8957 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -976,18 +976,46 @@ FSP_API NTSTATUS FspFileSystemOpSetSecurity(FSP_FILE_SYSTEM *FileSystem, (PSECURITY_DESCRIPTOR)Request->Buffer); } -FSP_API BOOLEAN FspFileSystemAddDirInfo(FSP_FSCTL_DIR_INFO *DirInfo, +FSP_API NTSTATUS FspFileSystemOpQueryStreamInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + NTSTATUS Result; + ULONG BytesTransferred; + + if (0 == FileSystem->Interface->GetStreamInfo) + return STATUS_INVALID_DEVICE_REQUEST; + + BytesTransferred = 0; + Result = FileSystem->Interface->GetStreamInfo(FileSystem, Request, + (PVOID)USERCONTEXT(Request->Req.QueryStreamInformation), + Response->Buffer, + FSP_FSCTL_TRANSACT_RSP_SIZEMAX - sizeof *Response, + &BytesTransferred); + if (!NT_SUCCESS(Result)) + return Result; + + Response->Size = (UINT16)(sizeof *Response + BytesTransferred); + Response->Rsp.QueryStreamInformation.Buffer.Offset = 0; + Response->Rsp.QueryStreamInformation.Buffer.Size = (UINT16)BytesTransferred; + return STATUS_SUCCESS; +} + +FSP_FSCTL_STATIC_ASSERT( + sizeof(UINT16) == sizeof ((FSP_FSCTL_DIR_INFO *)0)->Size && + sizeof(UINT16) == sizeof ((FSP_FSCTL_STREAM_INFO *)0)->Size, + "FSP_FSCTL_DIR_INFO::Size and FSP_FSCTL_STREAM_INFO::Size: sizeof must be 2."); +static BOOLEAN FspFileSystemAddXxxInfo(PVOID Info, SIZE_T InfoSize, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) { - static UINT8 Zero[sizeof DirInfo->Size] = { 0 }; + static UINT8 Zero[sizeof(UINT16)] = { 0 }; PVOID BufferEnd = (PUINT8)Buffer + Length; PVOID SrcBuffer; ULONG SrcLength, DstLength; - if (0 != DirInfo) + if (0 != Info) { - SrcBuffer = DirInfo; - SrcLength = DirInfo->Size; + SrcBuffer = Info; + SrcLength = *(PUINT16)Info; DstLength = FSP_FSCTL_DEFAULT_ALIGN_UP(SrcLength); } else @@ -1007,6 +1035,12 @@ FSP_API BOOLEAN FspFileSystemAddDirInfo(FSP_FSCTL_DIR_INFO *DirInfo, return TRUE; } +FSP_API BOOLEAN FspFileSystemAddDirInfo(FSP_FSCTL_DIR_INFO *DirInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + return FspFileSystemAddXxxInfo(DirInfo, sizeof *DirInfo, Buffer, Length, PBytesTransferred); +} + FSP_API BOOLEAN FspFileSystemFindReparsePoint(FSP_FILE_SYSTEM *FileSystem, NTSTATUS (*GetReparsePointByName)( FSP_FILE_SYSTEM *FileSystem, PVOID Context, @@ -1267,3 +1301,9 @@ FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint( else return STATUS_SUCCESS; } + +FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + return FspFileSystemAddXxxInfo(StreamInfo, sizeof *StreamInfo, Buffer, Length, PBytesTransferred); +} diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index 76c53b11..ab1b9c07 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -41,7 +41,6 @@ struct fsp_fuse_core_opt_data rellinks; int set_FileInfoTimeout; int CaseInsensitiveSearch, - NamedStreams, ReadOnlyVolume; FSP_FSCTL_VOLUME_PARAMS VolumeParams; }; @@ -96,9 +95,9 @@ static struct fuse_opt fsp_fuse_core_opts[] = FSP_FUSE_CORE_OPT("FileInfoTimeout=", set_FileInfoTimeout, 1), FSP_FUSE_CORE_OPT("FileInfoTimeout=%d", VolumeParams.FileInfoTimeout, 0), FSP_FUSE_CORE_OPT("CaseInsensitiveSearch", CaseInsensitiveSearch, 1), - FSP_FUSE_CORE_OPT("NamedStreams", NamedStreams, 1), FSP_FUSE_CORE_OPT("ReadOnlyVolume", ReadOnlyVolume, 1), FUSE_OPT_KEY("ReparsePoints", FUSE_OPT_KEY_DISCARD), + FUSE_OPT_KEY("NamedStreams", FUSE_OPT_KEY_DISCARD), FUSE_OPT_KEY("HardLinks", FUSE_OPT_KEY_DISCARD), FUSE_OPT_KEY("ExtendedAttributes", FUSE_OPT_KEY_DISCARD), FUSE_OPT_KEY("--UNC=", 'U'), @@ -457,7 +456,6 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key, " -o VolumeSerialNumber=N 32-bit wide\n" " -o FileInfoTimeout=N FileInfo/Security/VolumeInfo timeout (millisec)\n" " -o CaseInsensitiveSearch file system supports case-insensitive file names\n" - //" -o NamedStreams file system supports named streams\n" //" -o ReadOnlyVolume file system is read only\n" " --UNC=U --VolumePrefix=U UNC prefix (\\Server\\Share)\n" " --FileSystemName=FSN Name of user mode file system\n"); @@ -523,7 +521,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, opt_data.VolumeParams.PersistentAcls = TRUE; opt_data.VolumeParams.ReparsePoints = TRUE; opt_data.VolumeParams.ReparsePointsAccessCheck = FALSE; - opt_data.VolumeParams.NamedStreams = !!opt_data.NamedStreams; + opt_data.VolumeParams.NamedStreams = FALSE; opt_data.VolumeParams.ReadOnlyVolume = !!opt_data.ReadOnlyVolume; opt_data.VolumeParams.PostCleanupOnDeleteOnly = TRUE; opt_data.VolumeParams.UmFileNodeIsUserContext2 = TRUE; diff --git a/src/sys/device.c b/src/sys/device.c index 8254b637..c94ddad1 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -295,7 +295,7 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) NTSTATUS Result; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); LARGE_INTEGER IrpTimeout; - LARGE_INTEGER SecurityTimeout, DirInfoTimeout; + LARGE_INTEGER SecurityTimeout, DirInfoTimeout, StreamInfoTimeout; /* * Volume device initialization is a mess, because of the different ways of @@ -347,6 +347,16 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) return Result; FsvolDeviceExtension->InitDoneDir = 1; + /* create our stream info meta cache */ + StreamInfoTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.FileInfoTimeout); + /* convert millis to nanos */ + Result = FspMetaCacheCreate( + FspFsvolDeviceStreamInfoCacheCapacity, FspFsvolDeviceStreamInfoCacheItemSizeMax, &StreamInfoTimeout, + &FsvolDeviceExtension->StreamInfoCache); + if (!NT_SUCCESS(Result)) + return Result; + FsvolDeviceExtension->InitDoneStrm = 1; + /* initialize the FSRTL Notify mechanism */ Result = FspNotifyInitializeSync(&FsvolDeviceExtension->NotifySync); if (!NT_SUCCESS(Result)) @@ -406,6 +416,10 @@ static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject) FspNotifyUninitializeSync(&FsvolDeviceExtension->NotifySync); } + /* delete the stream info meta cache */ + if (FsvolDeviceExtension->InitDoneStrm) + FspMetaCacheDelete(FsvolDeviceExtension->StreamInfoCache); + /* delete the directory meta cache */ if (FsvolDeviceExtension->InitDoneDir) FspMetaCacheDelete(FsvolDeviceExtension->DirInfoCache); @@ -484,6 +498,7 @@ static VOID FspFsvolDeviceExpirationRoutine(PVOID Context) InterruptTime = KeQueryInterruptTime(); FspMetaCacheInvalidateExpired(FsvolDeviceExtension->SecurityCache, InterruptTime); FspMetaCacheInvalidateExpired(FsvolDeviceExtension->DirInfoCache, InterruptTime); + FspMetaCacheInvalidateExpired(FsvolDeviceExtension->StreamInfoCache, InterruptTime); FspIoqRemoveExpired(FsvolDeviceExtension->Ioq, InterruptTime); KeAcquireSpinLock(&FsvolDeviceExtension->ExpirationLock, &Irql); diff --git a/src/sys/driver.h b/src/sys/driver.h index 55af8430..c53e9e1a 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -722,6 +722,8 @@ enum FspFsvolDeviceSecurityCacheItemSizeMax = 4096, FspFsvolDeviceDirInfoCacheCapacity = 100, FspFsvolDeviceDirInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), + FspFsvolDeviceStreamInfoCacheCapacity = 100, + FspFsvolDeviceStreamInfoCacheItemSizeMax = 4096, }; typedef struct { @@ -748,7 +750,7 @@ typedef struct typedef struct { FSP_DEVICE_EXTENSION Base; - UINT32 InitDoneFsvrt:1, InitDoneIoq:1, InitDoneSec:1, InitDoneDir:1, + UINT32 InitDoneFsvrt:1, InitDoneIoq:1, InitDoneSec:1, InitDoneDir:1, InitDoneStrm:1, InitDoneCtxTab:1, InitDoneTimer:1, InitDoneInfo:1, InitDoneNotify:1; PDEVICE_OBJECT FsctlDeviceObject; PDEVICE_OBJECT FsvrtDeviceObject; @@ -760,6 +762,7 @@ typedef struct FSP_IOQ *Ioq; FSP_META_CACHE *SecurityCache; FSP_META_CACHE *DirInfoCache; + FSP_META_CACHE *StreamInfoCache; KSPIN_LOCK ExpirationLock; WORK_QUEUE_ITEM ExpirationWorkItem; BOOLEAN ExpirationInProgress; @@ -888,8 +891,9 @@ typedef struct ERESOURCE PagingIoResource; FAST_MUTEX HeaderFastMutex; SECTION_OBJECT_POINTERS SectionObjectPointers; - KSPIN_LOCK DirInfoSpinLock; - UINT64 DirInfo; /* allows to invalidate DirInfo w/o resources acquired */ + KSPIN_LOCK NpInfoSpinLock; /* allows to invalidate non-page Info w/o resources acquired */ + UINT64 DirInfo; + UINT64 StreamInfo; } FSP_FILE_NODE_NONPAGED; typedef struct { @@ -918,6 +922,7 @@ typedef struct UINT64 Security; ULONG SecurityChangeNumber; ULONG DirInfoChangeNumber; + ULONG StreamInfoChangeNumber; BOOLEAN TruncateOnClose; FILE_LOCK FileLock; struct @@ -1001,6 +1006,10 @@ BOOLEAN FspFileNodeReferenceDirInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PU VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG DirInfoChangeNumber); +BOOLEAN FspFileNodeReferenceStreamInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize); +VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); +BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, + ULONG StreamInfoChangeNumber); VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); NTSTATUS FspFileDescCreate(FSP_FILE_DESC **PFileDesc); @@ -1017,6 +1026,7 @@ NTSTATUS FspFileDescResetDirectoryPattern(FSP_FILE_DESC *FileDesc, #define FspFileNodeReleaseOwner(N,F,P) FspFileNodeReleaseOwnerF(N, FspFileNodeAcquire ## F, P) #define FspFileNodeDereferenceSecurity(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceDirInfo(P) FspMetaCacheDereferenceItemBuffer(P) +#define FspFileNodeDereferenceStreamInfo(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeUnlockAll(N,F,P) FsRtlFastUnlockAll(&(N)->FileLock, F, P, N) /* multiversion support */ diff --git a/src/sys/file.c b/src/sys/file.c index 92920763..29bdf0f9 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -57,6 +57,11 @@ VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG DirInfoChangeNumber); static VOID FspFileNodeInvalidateDirInfo(FSP_FILE_NODE *FileNode); +BOOLEAN FspFileNodeReferenceStreamInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize); +VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); +BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, + ULONG StreamInfoChangeNumber); +static VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode); VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); @@ -97,6 +102,10 @@ NTSTATUS FspFileDescResetDirectoryPattern(FSP_FILE_DESC *FileDesc, // !#pragma alloc_text(PAGE, FspFileNodeSetDirInfo) // !#pragma alloc_text(PAGE, FspFileNodeTrySetDirInfo) // !#pragma alloc_text(PAGE, FspFileNodeInvalidateDirInfo) +// !#pragma alloc_text(PAGE, FspFileNodeReferenceStreamInfo) +// !#pragma alloc_text(PAGE, FspFileNodeSetStreamInfo) +// !#pragma alloc_text(PAGE, FspFileNodeTrySetStreamInfo) +// !#pragma alloc_text(PAGE, FspFileNodeInvalidateStreamInfo) #pragma alloc_text(PAGE, FspFileNodeNotifyChange) #pragma alloc_text(PAGE, FspFileNodeProcessLockIrp) #pragma alloc_text(PAGE, FspFileNodeCompleteLockIrp) @@ -176,7 +185,7 @@ NTSTATUS FspFileNodeCreate(PDEVICE_OBJECT DeviceObject, ExInitializeResourceLite(&NonPaged->Resource); ExInitializeResourceLite(&NonPaged->PagingIoResource); ExInitializeFastMutex(&NonPaged->HeaderFastMutex); - KeInitializeSpinLock(&NonPaged->DirInfoSpinLock); + KeInitializeSpinLock(&NonPaged->NpInfoSpinLock); RtlZeroMemory(FileNode, sizeof *FileNode + ExtraSize); FileNode->Header.NodeTypeCode = FspFileNodeFileKind; @@ -211,6 +220,7 @@ VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode) FsRtlTeardownPerStreamContexts(&FileNode->Header); + FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, FileNode->NonPaged->StreamInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, FileNode->NonPaged->DirInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security); @@ -848,7 +858,7 @@ BOOLEAN FspFileNodeReferenceDirInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PU FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; UINT64 DirInfo; - /* no need to acquire the DirInfoSpinLock as the FileNode is acquired */ + /* no need to acquire the NpInfoSpinLock as the FileNode is acquired */ DirInfo = NonPaged->DirInfo; return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->DirInfoCache, @@ -865,7 +875,7 @@ VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size) KIRQL Irql; UINT64 DirInfo; - /* no need to acquire the DirInfoSpinLock as the FileNode is acquired */ + /* no need to acquire the NpInfoSpinLock as the FileNode is acquired */ DirInfo = NonPaged->DirInfo; FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, DirInfo); @@ -873,10 +883,10 @@ VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size) FspMetaCacheAddItem(FsvolDeviceExtension->DirInfoCache, Buffer, Size) : 0; FileNode->DirInfoChangeNumber++; - /* acquire the DirInfoSpinLock to protect against concurrent FspFileNodeInvalidateDirInfo */ - KeAcquireSpinLock(&NonPaged->DirInfoSpinLock, &Irql); + /* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeInvalidateDirInfo */ + KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql); NonPaged->DirInfo = DirInfo; - KeReleaseSpinLock(&NonPaged->DirInfoSpinLock, Irql); + KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql); } BOOLEAN FspFileNodeTrySetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, @@ -901,14 +911,84 @@ static VOID FspFileNodeInvalidateDirInfo(FSP_FILE_NODE *FileNode) KIRQL Irql; UINT64 DirInfo; - /* acquire the DirInfoSpinLock to protect against concurrent FspFileNodeSetDirInfo */ - KeAcquireSpinLock(&NonPaged->DirInfoSpinLock, &Irql); + /* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeSetDirInfo */ + KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql); DirInfo = NonPaged->DirInfo; - KeReleaseSpinLock(&NonPaged->DirInfoSpinLock, Irql); + KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql); FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, DirInfo); } +BOOLEAN FspFileNodeReferenceStreamInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize) +{ + // !PAGED_CODE(); + + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = + FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + UINT64 StreamInfo; + + /* no need to acquire the NpInfoSpinLock as the FileNode is acquired */ + StreamInfo = NonPaged->StreamInfo; + + return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->StreamInfoCache, + StreamInfo, PBuffer, PSize); +} + +VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size) +{ + // !PAGED_CODE(); + + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = + FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + KIRQL Irql; + UINT64 StreamInfo; + + /* no need to acquire the NpInfoSpinLock as the FileNode is acquired */ + StreamInfo = NonPaged->StreamInfo; + + FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, StreamInfo); + StreamInfo = 0 != Buffer ? + FspMetaCacheAddItem(FsvolDeviceExtension->StreamInfoCache, Buffer, Size) : 0; + FileNode->StreamInfoChangeNumber++; + + /* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeInvalidateStreamInfo */ + KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql); + NonPaged->StreamInfo = StreamInfo; + KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql); +} + +BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, + ULONG StreamInfoChangeNumber) +{ + // !PAGED_CODE(); + + if (FileNode->StreamInfoChangeNumber != StreamInfoChangeNumber) + return FALSE; + + FspFileNodeSetStreamInfo(FileNode, Buffer, Size); + return TRUE; +} + +static VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode) +{ + // !PAGED_CODE(); + + PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + KIRQL Irql; + UINT64 StreamInfo; + + /* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeSetStreamInfo */ + KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql); + StreamInfo = NonPaged->StreamInfo; + KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql); + + FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, StreamInfo); +} + VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action) { diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index b3816acb..2367c725 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -38,6 +38,13 @@ static NTSTATUS FspFsvolQueryPositionInformation(PFILE_OBJECT FileObject, static NTSTATUS FspFsvolQueryStandardInformation(PFILE_OBJECT FileObject, PVOID *PBuffer, PVOID BufferEnd, const FSP_FSCTL_FILE_INFO *FileInfo); +static NTSTATUS FspFsvolQueryStreamInformationCopy( + FSP_FSCTL_STREAM_INFO *StreamInfoBuffer, ULONG StreamInfoBufferSize, + PVOID DestBuf, PULONG PDestLen); +static NTSTATUS FspFsvolQueryStreamInformation( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +static NTSTATUS FspFsvolQueryStreamInformationSuccess( + PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response); static NTSTATUS FspFsvolQueryInformation( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolQueryInformationComplete; @@ -78,6 +85,9 @@ FSP_DRIVER_DISPATCH FspSetInformation; #pragma alloc_text(PAGE, FspFsvolQueryNetworkOpenInformation) #pragma alloc_text(PAGE, FspFsvolQueryPositionInformation) #pragma alloc_text(PAGE, FspFsvolQueryStandardInformation) +#pragma alloc_text(PAGE, FspFsvolQueryStreamInformationCopy) +#pragma alloc_text(PAGE, FspFsvolQueryStreamInformation) +#pragma alloc_text(PAGE, FspFsvolQueryStreamInformationSuccess) #pragma alloc_text(PAGE, FspFsvolQueryInformation) #pragma alloc_text(PAGE, FspFsvolQueryInformationComplete) #pragma alloc_text(PAGE, FspFsvolQueryInformationRequestFini) @@ -349,6 +359,197 @@ static NTSTATUS FspFsvolQueryStandardInformation(PFILE_OBJECT FileObject, return STATUS_SUCCESS; } +static NTSTATUS FspFsvolQueryStreamInformationCopy( + FSP_FSCTL_STREAM_INFO *StreamInfo, ULONG StreamInfoSize, + PVOID DestBuf, PULONG PDestLen) +{ +#define FILL_INFO()\ + do\ + {\ + FILE_STREAM_INFORMATION InfoStruct = { 0 }, *Info = &InfoStruct;\ + Info->NextEntryOffset = 0;\ + Info->StreamNameLength = StreamNameLength;\ + Info->StreamSize.QuadPart = StreamInfo->StreamSize;\ + Info->StreamAllocationSize.QuadPart = StreamInfo->StreamAllocationSize;\ + Info = DestBuf;\ + RtlCopyMemory(Info, &InfoStruct, FIELD_OFFSET(FILE_STREAM_INFORMATION, StreamName));\ + RtlCopyMemory(Info->StreamName, StreamInfo->StreamNameBuf, StreamNameLength);\ + } while (0,0) + + PAGED_CODE(); + + NTSTATUS Result = STATUS_SUCCESS; + PUINT8 StreamInfoEnd = (PUINT8)StreamInfo + StreamInfoSize; + PUINT8 DestBufBgn = (PUINT8)DestBuf; + PUINT8 DestBufEnd = (PUINT8)DestBuf + *PDestLen; + PVOID PrevDestBuf = 0; + ULONG StreamNameLength; + ULONG BaseInfoLen = FIELD_OFFSET(FILE_STREAM_INFORMATION, StreamName); + + *PDestLen = 0; + + for (; + NT_SUCCESS(Result) && (PUINT8)StreamInfo + sizeof(StreamInfo->Size) <= StreamInfoEnd; + StreamInfo = (PVOID)((PUINT8)StreamInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(StreamInfoSize))) + { + StreamInfoSize = StreamInfo->Size; + + if (sizeof(FSP_FSCTL_STREAM_INFO) > StreamInfoSize) + break; + + if ((PUINT8)DestBuf + BaseInfoLen > DestBufEnd) + { + if (0 == *PDestLen) + /* if we haven't copied anything yet, buffer is too small */ + return STATUS_BUFFER_TOO_SMALL; + else + /* *PDestLen contains bytes copied so far */ + return STATUS_BUFFER_OVERFLOW; + } + + StreamNameLength = StreamInfoSize - sizeof(FSP_FSCTL_STREAM_INFO); + + if ((PUINT8)DestBuf + BaseInfoLen + StreamNameLength > DestBufEnd) + { + /* only copy as much of the stream name as we can and return STATUS_BUFFER_OVERFLOW */ + StreamNameLength = (ULONG)(DestBufEnd - ((PUINT8)DestBuf + BaseInfoLen)); + Result = STATUS_BUFFER_OVERFLOW; + } + + /* fill in NextEntryOffset */ + if (0 != PrevDestBuf) + *(PULONG)PrevDestBuf = (ULONG)((PUINT8)DestBuf - (PUINT8)PrevDestBuf); + PrevDestBuf = DestBuf; + + FILL_INFO(); + + /* bytes copied so far */ + *PDestLen = (ULONG)((PUINT8)DestBuf + BaseInfoLen + StreamNameLength - DestBufBgn); + + /* advance DestBuf; make sure to align properly! */ + DestBuf = (PVOID)((PUINT8)DestBuf + + FSP_FSCTL_ALIGN_UP(BaseInfoLen + StreamNameLength, sizeof(LONGLONG))); + } + + return Result; + +#undef FILL_INFO +} + +static NTSTATUS FspFsvolQueryStreamInformation( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + NTSTATUS Result; + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; + ULONG Length = IrpSp->Parameters.QueryFile.Length; + PVOID StreamInfoBuffer; + ULONG StreamInfoBufferSize; + FSP_FSCTL_TRANSACT_REQ *Request; + + ASSERT(FileNode == FileDesc->FileNode); + + FspFileNodeAcquireShared(FileNode, Main); + if (FspFileNodeReferenceStreamInfo(FileNode, &StreamInfoBuffer, &StreamInfoBufferSize)) + { + FspFileNodeRelease(FileNode, Main); + + Result = FspFsvolQueryStreamInformationCopy( + StreamInfoBuffer, StreamInfoBufferSize, Buffer, &Length); + + FspFileNodeDereferenceStreamInfo(StreamInfoBuffer); + + Irp->IoStatus.Information = Length; + return Result; + } + + FspFileNodeAcquireShared(FileNode, Pgio); + + Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolQueryInformationRequestFini, &Request); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Full); + return Result; + } + + Request->Kind = FspFsctlTransactQueryStreamInformationKind; + Request->Req.QueryStreamInformation.UserContext = FileNode->UserContext; + Request->Req.QueryStreamInformation.UserContext2 = FileDesc->UserContext2; + + FspFileNodeSetOwner(FileNode, Full, Request); + FspIopRequestContext(Request, RequestFileNode) = FileNode; + + return FSP_STATUS_IOQ_POST; +} + +static NTSTATUS FspFsvolQueryStreamInformationSuccess( + PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response) +{ + PAGED_CODE(); + + NTSTATUS Result; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; + ULONG Length = IrpSp->Parameters.QueryFile.Length; + PVOID StreamInfoBuffer = 0; + ULONG StreamInfoBufferSize = 0; + FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); + BOOLEAN Success; + + if (0 != FspIopRequestContext(Request, RequestFileNode)) + { + /* check that the stream info we got back is valid */ + if (Response->Buffer + Response->Rsp.QueryStreamInformation.Buffer.Size > + (PUINT8)Response + Response->Size) + { + Irp->IoStatus.Information = 0; + return STATUS_INFO_LENGTH_MISMATCH; /* ???: what is the best code to return here? */ + } + + FspIopRequestContext(Request, RequestInfoChangeNumber) = (PVOID)FileNode->StreamInfoChangeNumber; + FspIopRequestContext(Request, RequestFileNode) = 0; + + FspFileNodeReleaseOwner(FileNode, Full, Request); + } + + Success = DEBUGTEST(90) && FspFileNodeTryAcquireExclusive(FileNode, Main); + if (!Success) + { + FspIopRetryCompleteIrp(Irp, Response, &Result); + return Result; + } + + Success = !FspFileNodeTrySetStreamInfo(FileNode, + Response->Buffer, Response->Rsp.QueryStreamInformation.Buffer.Size, + (ULONG)(UINT_PTR)FspIopRequestContext(Request, RequestInfoChangeNumber)); + Success = Success && + FspFileNodeReferenceStreamInfo(FileNode, &StreamInfoBuffer, &StreamInfoBufferSize); + FspFileNodeRelease(FileNode, Main); + if (Success) + { + Result = FspFsvolQueryStreamInformationCopy( + StreamInfoBuffer, StreamInfoBufferSize, Buffer, &Length); + FspFileNodeDereferenceStreamInfo(StreamInfoBuffer); + } + else + { + StreamInfoBuffer = (PVOID)Response->Buffer; + StreamInfoBufferSize = Response->Rsp.QueryStreamInformation.Buffer.Size; + Result = FspFsvolQueryStreamInformationCopy( + StreamInfoBuffer, StreamInfoBufferSize, Buffer, &Length); + } + + Irp->IoStatus.Information = Length; + + return Result; +} + static NTSTATUS FspFsvolQueryInformation( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -358,8 +559,13 @@ static NTSTATUS FspFsvolQueryInformation( if (!FspFileNodeIsValid(IrpSp->FileObject->FsContext)) return STATUS_INVALID_DEVICE_REQUEST; - NTSTATUS Result; FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; + + /* special case FileStreamInformation */ + if (FileStreamInformation == FileInformationClass) + return FspFsvolQueryStreamInformation(FsvolDeviceObject, Irp, IrpSp); + + NTSTATUS Result; PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; FSP_FILE_DESC *FileDesc = FileObject->FsContext2; @@ -415,9 +621,6 @@ static NTSTATUS FspFsvolQueryInformation( case FileStandardInformation: Result = FspFsvolQueryStandardInformation(FileObject, &Buffer, BufferEnd, 0); break; - case FileStreamInformation: - Result = STATUS_INVALID_PARAMETER; /* !!!: no stream support yet! */ - return Result; default: Result = STATUS_INVALID_PARAMETER; return Result; @@ -496,6 +699,11 @@ NTSTATUS FspFsvolQueryInformationComplete( } FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; + + /* special case FileStreamInformation */ + if (FileStreamInformation == FileInformationClass) + FSP_RETURN(Result = FspFsvolQueryStreamInformationSuccess(Irp, Response)); + PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; PVOID Buffer = Irp->AssociatedIrp.SystemBuffer;