From 8d38a0dac659660039a229b513dd6b46b1c8831b Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 17 Oct 2016 14:23:56 -0700 Subject: [PATCH] sys,dll: support file name normalization --- inc/winfsp/fsctl.h | 12 +++- inc/winfsp/winfsp.h | 48 +++++++++++++ src/dll/fsop.c | 120 ++++++++++++++++++++++++-------- src/sys/create.c | 32 ++++++++- src/sys/device.c | 5 ++ src/sys/driver.h | 5 ++ tst/memfs/memfs.cpp | 59 +++++++++++++++- tst/winfsp-tests/reparse-test.c | 12 ++-- 8 files changed, 256 insertions(+), 37 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index ff5fa5a4..e5f0d7da 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -28,7 +28,7 @@ extern "C" { #if defined(WINFSP_SYS_INTERNAL) || defined(WINFSP_DLL_INTERNAL) #define FSP_FSCTL_STATIC_ASSERT(e,m) static_assert(e,m) #else -#define FSP_FSCTL_STATIC_ASSERT(e,m) +#define FSP_FSCTL_STATIC_ASSERT(e,m) static_assert(1,"") #endif #define FSP_FSCTL_DRIVER_NAME "WinFsp" @@ -176,6 +176,12 @@ typedef struct UINT64 IndexNumber; } FSP_FSCTL_FILE_INFO; typedef struct +{ + FSP_FSCTL_FILE_INFO FileInfo; + PWSTR NormalizedName; + UINT16 NormalizedNameSize; +} FSP_FSCTL_OPEN_FILE_INFO; +typedef struct { UINT16 Size; FSP_FSCTL_FILE_INFO FileInfo; @@ -374,6 +380,7 @@ typedef struct UINT64 UserContext2; /* user context associated with file descriptor (handle) */ UINT32 GrantedAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_TRANSACT_BUF FileName; } Opened; /* IoStatus.Status == STATUS_REPARSE */ struct @@ -425,6 +432,9 @@ typedef struct FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[]; } FSP_FSCTL_TRANSACT_RSP; #pragma warning(pop) +FSP_FSCTL_STATIC_ASSERT(FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX > FSP_FSCTL_TRANSACT_PATH_SIZEMAX, + "FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX must be greater than FSP_FSCTL_TRANSACT_PATH_SIZEMAX " + "to detect when a normalized name has been set during a Create/Open request."); static inline BOOLEAN FspFsctlTransactCanProduceRequest( FSP_FSCTL_TRANSACT_REQ *Request, PVOID RequestBufEnd) { diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 31235276..db7020ea 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1040,6 +1040,54 @@ FSP_API NTSTATUS FspFileSystemOpQueryStreamInformation(FSP_FILE_SYSTEM *FileSyst /* * Helpers */ +/** + * Get open information buffer. + * + * This is a helper for implementing the Create and Open operations. It cannot be used with + * any other operations. + * + * The FileInfo parameter to Create and Open is typed as pointer to FSP_FSCTL_FILE_INFO. The + * true type of this parameter is pointer to FSP_FSCTL_OPEN_FILE_INFO. This simple function + * converts from one type to the other. + * + * The FSP_FSCTL_OPEN_FILE_INFO type contains a FSP_FSCTL_FILE_INFO as well as the fields + * NormalizedName and NormalizedNameSize. These fields can be used for file name normalization. + * File name normalization is used to ensure that the FSD and the OS know the correct case + * of a newly opened file name. + * + * For case-sensitive file systems this functionality should be ignored. The FSD will always + * assume that the normalized file name is the same as the file name used to open the file. + * + * For case-insensitive file systems this functionality may be ignored. In this case the FSD + * will assume that the normalized file name is the upper case version of the file name used + * to open the file. The file system will work correctly and the only way an application will + * be able to tell that the file system does not preserve case in normalized file names is by + * issuing a GetFinalPathNameByHandle API call (or NtQueryInformationFile with + * FileNameInformation/FileNormalizedNameInformation). + * + * For case-insensitive file systems this functionality may also be used. In this case the + * user mode file system may use the NormalizedName and NormalizedNameSize parameters to + * report to the FSD the normalized file name. It should be noted that the normalized file + * name may only differ in case from the file name used to open the file. The NormalizedName + * field will point to a buffer that can receive the normalized file name. The + * NormalizedNameSize field will contain the size of the normalized file name buffer. On + * completion of the Create or Open operation it should contain the actual size of the + * normalized file name copied into the normalized file name buffer. The normalized file name + * should not contain a terminating zero. + * + * @param FileInfo + * The FileInfo parameter as passed to Create or Open operation. + * @return + * A pointer to the open information buffer for this Create or Open operation. + * @see + * Create + * Open + */ +static inline +FSP_FSCTL_OPEN_FILE_INFO *FspFileSystemGetOpenFileInfo(FSP_FSCTL_FILE_INFO *FileInfo) +{ + return (FSP_FSCTL_OPEN_FILE_INFO *)FileInfo; +} /** * Add directory information to a buffer. * diff --git a/src/dll/fsop.c b/src/dll/fsop.c index aafa0f0c..8a44ae07 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -353,7 +353,7 @@ static NTSTATUS FspFileSystemOpCreate_FileCreate(FSP_FILE_SYSTEM *FileSystem, UINT32 GrantedAccess; PSECURITY_DESCRIPTOR ParentDescriptor, ObjectDescriptor; PVOID FileNode; - FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_OPEN_FILE_INFO OpenFileInfo; Result = FspFileSystemCreateCheck(FileSystem, Request, Response, TRUE, &GrantedAccess, &ParentDescriptor); @@ -366,19 +366,29 @@ static NTSTATUS FspFileSystemOpCreate_FileCreate(FSP_FILE_SYSTEM *FileSystem, return Result; FileNode = 0; - memset(&FileInfo, 0, sizeof FileInfo); + memset(&OpenFileInfo, 0, sizeof OpenFileInfo); + OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; + OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; Result = FileSystem->Interface->Create(FileSystem, Request, (PWSTR)Request->Buffer, Request->Req.Create.CaseSensitive, Request->Req.Create.CreateOptions, Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, - &FileNode, &FileInfo); + &FileNode, &OpenFileInfo.FileInfo); FspDeleteSecurityDescriptor(ObjectDescriptor, FspCreateSecurityDescriptor); if (!NT_SUCCESS(Result)) return Result; + if (FSP_FSCTL_TRANSACT_PATH_SIZEMAX >= OpenFileInfo.NormalizedNameSize) + { + Response->Size = (UINT16)(sizeof *Response + OpenFileInfo.NormalizedNameSize); + Response->Rsp.Create.Opened.FileName.Offset = 0; + Response->Rsp.Create.Opened.FileName.Size = (UINT16)OpenFileInfo.NormalizedNameSize; + } + Response->IoStatus.Information = FILE_CREATED; USERCONTEXT(Response->Rsp.Create.Opened) = (UINT_PTR)FileNode; Response->Rsp.Create.Opened.GrantedAccess = GrantedAccess; - memcpy(&Response->Rsp.Create.Opened.FileInfo, &FileInfo, sizeof FileInfo); + memcpy(&Response->Rsp.Create.Opened.FileInfo, + &OpenFileInfo.FileInfo, sizeof OpenFileInfo.FileInfo); return STATUS_SUCCESS; } @@ -388,24 +398,34 @@ static NTSTATUS FspFileSystemOpCreate_FileOpen(FSP_FILE_SYSTEM *FileSystem, NTSTATUS Result; UINT32 GrantedAccess; PVOID FileNode; - FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_OPEN_FILE_INFO OpenFileInfo; Result = FspFileSystemOpenCheck(FileSystem, Request, Response, TRUE, &GrantedAccess); if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result) return Result; FileNode = 0; - memset(&FileInfo, 0, sizeof FileInfo); + memset(&OpenFileInfo, 0, sizeof OpenFileInfo); + OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; + OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; Result = FileSystem->Interface->Open(FileSystem, Request, (PWSTR)Request->Buffer, Request->Req.Create.CaseSensitive, Request->Req.Create.CreateOptions, - &FileNode, &FileInfo); + &FileNode, &OpenFileInfo.FileInfo); if (!NT_SUCCESS(Result)) return Result; + if (FSP_FSCTL_TRANSACT_PATH_SIZEMAX >= OpenFileInfo.NormalizedNameSize) + { + Response->Size = (UINT16)(sizeof *Response + OpenFileInfo.NormalizedNameSize); + Response->Rsp.Create.Opened.FileName.Offset = 0; + Response->Rsp.Create.Opened.FileName.Size = (UINT16)OpenFileInfo.NormalizedNameSize; + } + Response->IoStatus.Information = FILE_OPENED; USERCONTEXT(Response->Rsp.Create.Opened) = (UINT_PTR)FileNode; Response->Rsp.Create.Opened.GrantedAccess = GrantedAccess; - memcpy(&Response->Rsp.Create.Opened.FileInfo, &FileInfo, sizeof FileInfo); + memcpy(&Response->Rsp.Create.Opened.FileInfo, + &OpenFileInfo.FileInfo, sizeof OpenFileInfo.FileInfo); return STATUS_SUCCESS; } @@ -416,7 +436,7 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem, UINT32 GrantedAccess; PSECURITY_DESCRIPTOR ParentDescriptor, ObjectDescriptor; PVOID FileNode; - FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_OPEN_FILE_INFO OpenFileInfo; BOOLEAN Create = FALSE; Result = FspFileSystemOpenCheck(FileSystem, Request, Response, TRUE, &GrantedAccess); @@ -430,10 +450,12 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem, if (!Create) { FileNode = 0; - memset(&FileInfo, 0, sizeof FileInfo); + memset(&OpenFileInfo, 0, sizeof OpenFileInfo); + OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; + OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; Result = FileSystem->Interface->Open(FileSystem, Request, (PWSTR)Request->Buffer, Request->Req.Create.CaseSensitive, Request->Req.Create.CreateOptions, - &FileNode, &FileInfo); + &FileNode, &OpenFileInfo.FileInfo); if (!NT_SUCCESS(Result)) { if (STATUS_OBJECT_NAME_NOT_FOUND != Result) @@ -455,20 +477,30 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem, return Result; FileNode = 0; - memset(&FileInfo, 0, sizeof FileInfo); + memset(&OpenFileInfo, 0, sizeof OpenFileInfo); + OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; + OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; Result = FileSystem->Interface->Create(FileSystem, Request, (PWSTR)Request->Buffer, Request->Req.Create.CaseSensitive, Request->Req.Create.CreateOptions, Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, - &FileNode, &FileInfo); + &FileNode, &OpenFileInfo.FileInfo); FspDeleteSecurityDescriptor(ObjectDescriptor, FspCreateSecurityDescriptor); if (!NT_SUCCESS(Result)) return Result; } + if (FSP_FSCTL_TRANSACT_PATH_SIZEMAX >= OpenFileInfo.NormalizedNameSize) + { + Response->Size = (UINT16)(sizeof *Response + OpenFileInfo.NormalizedNameSize); + Response->Rsp.Create.Opened.FileName.Offset = 0; + Response->Rsp.Create.Opened.FileName.Size = (UINT16)OpenFileInfo.NormalizedNameSize; + } + Response->IoStatus.Information = Create ? FILE_CREATED : FILE_OPENED; USERCONTEXT(Response->Rsp.Create.Opened) = (UINT_PTR)FileNode; Response->Rsp.Create.Opened.GrantedAccess = GrantedAccess; - memcpy(&Response->Rsp.Create.Opened.FileInfo, &FileInfo, sizeof FileInfo); + memcpy(&Response->Rsp.Create.Opened.FileInfo, + &OpenFileInfo.FileInfo, sizeof OpenFileInfo.FileInfo); return STATUS_SUCCESS; } @@ -478,7 +510,7 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwrite(FSP_FILE_SYSTEM *FileSystem, NTSTATUS Result; UINT32 GrantedAccess; PVOID FileNode; - FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_OPEN_FILE_INFO OpenFileInfo; BOOLEAN Supersede = FILE_SUPERSEDE == ((Request->Req.Create.CreateOptions >> 24) & 0xff); Result = FspFileSystemOverwriteCheck(FileSystem, Request, Response, TRUE, &GrantedAccess); @@ -486,17 +518,27 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwrite(FSP_FILE_SYSTEM *FileSystem, return Result; FileNode = 0; - memset(&FileInfo, 0, sizeof FileInfo); + memset(&OpenFileInfo, 0, sizeof OpenFileInfo); + OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; + OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; Result = FileSystem->Interface->Open(FileSystem, Request, (PWSTR)Request->Buffer, Request->Req.Create.CaseSensitive, Request->Req.Create.CreateOptions, - &FileNode, &FileInfo); + &FileNode, &OpenFileInfo.FileInfo); if (!NT_SUCCESS(Result)) return Result; + if (FSP_FSCTL_TRANSACT_PATH_SIZEMAX >= OpenFileInfo.NormalizedNameSize) + { + Response->Size = (UINT16)(sizeof *Response + OpenFileInfo.NormalizedNameSize); + Response->Rsp.Create.Opened.FileName.Offset = 0; + Response->Rsp.Create.Opened.FileName.Size = (UINT16)OpenFileInfo.NormalizedNameSize; + } + Response->IoStatus.Information = Supersede ? FILE_SUPERSEDED : FILE_OVERWRITTEN; USERCONTEXT(Response->Rsp.Create.Opened) = (UINT_PTR)FileNode; Response->Rsp.Create.Opened.GrantedAccess = GrantedAccess; - memcpy(&Response->Rsp.Create.Opened.FileInfo, &FileInfo, sizeof FileInfo); + memcpy(&Response->Rsp.Create.Opened.FileInfo, + &OpenFileInfo.FileInfo, sizeof OpenFileInfo.FileInfo); return STATUS_SUCCESS; } @@ -507,7 +549,7 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste UINT32 GrantedAccess; PSECURITY_DESCRIPTOR ParentDescriptor, ObjectDescriptor; PVOID FileNode; - FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_OPEN_FILE_INFO OpenFileInfo; BOOLEAN Create = FALSE; Result = FspFileSystemOverwriteCheck(FileSystem, Request, Response, TRUE, &GrantedAccess); @@ -521,10 +563,12 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste if (!Create) { FileNode = 0; - memset(&FileInfo, 0, sizeof FileInfo); + memset(&OpenFileInfo, 0, sizeof OpenFileInfo); + OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; + OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; Result = FileSystem->Interface->Open(FileSystem, Request, (PWSTR)Request->Buffer, Request->Req.Create.CaseSensitive, Request->Req.Create.CreateOptions, - &FileNode, &FileInfo); + &FileNode, &OpenFileInfo.FileInfo); if (!NT_SUCCESS(Result)) { if (STATUS_OBJECT_NAME_NOT_FOUND != Result) @@ -546,20 +590,30 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste return Result; FileNode = 0; - memset(&FileInfo, 0, sizeof FileInfo); + memset(&OpenFileInfo, 0, sizeof OpenFileInfo); + OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; + OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; Result = FileSystem->Interface->Create(FileSystem, Request, (PWSTR)Request->Buffer, Request->Req.Create.CaseSensitive, Request->Req.Create.CreateOptions, Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, - &FileNode, &FileInfo); + &FileNode, &OpenFileInfo.FileInfo); FspDeleteSecurityDescriptor(ObjectDescriptor, FspCreateSecurityDescriptor); if (!NT_SUCCESS(Result)) return Result; } + if (FSP_FSCTL_TRANSACT_PATH_SIZEMAX >= OpenFileInfo.NormalizedNameSize) + { + Response->Size = (UINT16)(sizeof *Response + OpenFileInfo.NormalizedNameSize); + Response->Rsp.Create.Opened.FileName.Offset = 0; + Response->Rsp.Create.Opened.FileName.Size = (UINT16)OpenFileInfo.NormalizedNameSize; + } + Response->IoStatus.Information = Create ? FILE_CREATED : FILE_OVERWRITTEN; USERCONTEXT(Response->Rsp.Create.Opened) = (UINT_PTR)FileNode; Response->Rsp.Create.Opened.GrantedAccess = GrantedAccess; - memcpy(&Response->Rsp.Create.Opened.FileInfo, &FileInfo, sizeof FileInfo); + memcpy(&Response->Rsp.Create.Opened.FileInfo, + &OpenFileInfo.FileInfo, sizeof OpenFileInfo.FileInfo); return STATUS_SUCCESS; } @@ -571,7 +625,7 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenTargetDirectory(FSP_FILE_SYSTEM *F PWSTR Parent, Suffix; UINT32 GrantedAccess; PVOID FileNode; - FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_OPEN_FILE_INFO OpenFileInfo; UINT32 Information; Result = FspFileSystemOpenTargetDirectoryCheck(FileSystem, Request, Response, &GrantedAccess); @@ -579,11 +633,13 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenTargetDirectory(FSP_FILE_SYSTEM *F return Result; FileNode = 0; - memset(&FileInfo, 0, sizeof FileInfo); + memset(&OpenFileInfo, 0, sizeof OpenFileInfo); + OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; + OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; FspPathSuffix((PWSTR)Request->Buffer, &Parent, &Suffix, Root); Result = FileSystem->Interface->Open(FileSystem, Request, Parent, Request->Req.Create.CaseSensitive, Request->Req.Create.CreateOptions, - &FileNode, &FileInfo); + &FileNode, &OpenFileInfo.FileInfo); FspPathCombine((PWSTR)Request->Buffer, Suffix); if (!NT_SUCCESS(Result)) return Result; @@ -595,10 +651,18 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenTargetDirectory(FSP_FILE_SYSTEM *F Information = NT_SUCCESS(Result) ? FILE_EXISTS : FILE_DOES_NOT_EXIST; } + if (FSP_FSCTL_TRANSACT_PATH_SIZEMAX >= OpenFileInfo.NormalizedNameSize) + { + Response->Size = (UINT16)(sizeof *Response + OpenFileInfo.NormalizedNameSize); + Response->Rsp.Create.Opened.FileName.Offset = 0; + Response->Rsp.Create.Opened.FileName.Size = (UINT16)OpenFileInfo.NormalizedNameSize; + } + Response->IoStatus.Information = Information; USERCONTEXT(Response->Rsp.Create.Opened) = (UINT_PTR)FileNode; Response->Rsp.Create.Opened.GrantedAccess = GrantedAccess; - memcpy(&Response->Rsp.Create.Opened.FileInfo, &FileInfo, sizeof FileInfo); + memcpy(&Response->Rsp.Create.Opened.FileInfo, + &OpenFileInfo.FileInfo, sizeof OpenFileInfo.FileInfo); return STATUS_SUCCESS; } diff --git a/src/sys/create.c b/src/sys/create.c index 913ccc1d..3aa3c607 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -488,7 +488,7 @@ static NTSTATUS FspFsvolCreateNoLock( RtlCopyMemory(Request->Buffer + Request->Req.Create.SecurityDescriptor.Offset, SecurityDescriptor, SecurityDescriptorSize); - /* fix FileNode->FileName if we were doing SL_OPEN_TARGET_DIRECTORY */ + /* fix FileNode->FileName if we are doing SL_OPEN_TARGET_DIRECTORY */ if (Request->Req.Create.OpenTargetDirectory) { UNICODE_STRING Suffix; @@ -605,6 +605,7 @@ NTSTATUS FspFsvolCreateComplete( FSP_FILE_DESC *FileDesc = FspIopRequestContext(Request, RequestFileDesc); FSP_FILE_NODE *FileNode = FileDesc->FileNode; FSP_FILE_NODE *OpenedFileNode; + UNICODE_STRING NormalizedName; PREPARSE_DATA_BUFFER ReparseData; UNICODE_STRING ReparseTargetPrefix0, ReparseTargetPrefix1, ReparseTargetPath; @@ -760,6 +761,35 @@ NTSTATUS FspFsvolCreateComplete( } /* populate the FileNode/FileDesc fields from the Response */ + if (!FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch) + { + /* is there a normalized file name as part of the response? */ + if (0 == Response->Rsp.Create.Opened.FileName.Size) + { + /* if not, the default is to upper case the name */ + + FspFileNameUpcase(&FileNode->FileName, &FileNode->FileName, 0); + } + else + { + /* if yes, verify it and then set it */ + + if (Response->Buffer + Response->Rsp.Create.Opened.FileName.Size > + (PUINT8)Response + Response->Size) + FSP_RETURN(Result = STATUS_OBJECT_NAME_INVALID); + + NormalizedName.Length = NormalizedName.MaximumLength = + Response->Rsp.Create.Opened.FileName.Size; + NormalizedName.Buffer = (PVOID)Response->Buffer; + + /* normalized file name can only differ in case from requested one */ + if (0 != FspFileNameCompare(&FileNode->FileName, &NormalizedName, TRUE, 0)) + FSP_RETURN(Result = STATUS_OBJECT_NAME_INVALID); + + ASSERT(FileNode->FileName.Length == NormalizedName.Length); + RtlCopyMemory(FileNode->FileName.Buffer, NormalizedName.Buffer, NormalizedName.Length); + } + } FileNode->UserContext = Response->Rsp.Create.Opened.UserContext; FileNode->IndexNumber = Response->Rsp.Create.Opened.FileInfo.IndexNumber; FileNode->IsDirectory = BooleanFlagOn(Response->Rsp.Create.Opened.FileInfo.FileAttributes, diff --git a/src/sys/device.c b/src/sys/device.c index 4f3d697c..283136fc 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -699,6 +699,11 @@ static RTL_GENERIC_COMPARE_RESULTS NTAPI FspFsvolDeviceCompareContextByName( PUNICODE_STRING SecondFileName = *(PUNICODE_STRING *)SecondElement; LONG ComparisonResult; + /* + * Since FileNode FileName's are now always normalized, we could perhaps get away + * with using CaseInsensitive == FALSE at all times. For safety reasons we avoid + * doing so here. + */ ComparisonResult = FspFileNameCompare(FirstFileName, SecondFileName, CaseInsensitive, 0); if (0 > ComparisonResult) diff --git a/src/sys/driver.h b/src/sys/driver.h index 5e3af46f..24aef312 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -437,6 +437,10 @@ BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, PUNICODE_STRING StreamPart, PUL BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern); VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); #if 0 +NTSTATUS FspFileNameUpcase( + PUNICODE_STRING DestinationName, + PUNICODE_STRING SourceName, + PCWCH UpcaseTable); LONG FspFileNameCompare( PUNICODE_STRING Name1, PUNICODE_STRING Name2, @@ -448,6 +452,7 @@ BOOLEAN FspFileNameIsPrefix( BOOLEAN IgnoreCase, PCWCH UpcaseTable); #else +#define FspFileNameUpcase(D,S,U) (ASSERT(0 == (U)), RtlUpcaseUnicodeString(D,S,FALSE)) #define FspFileNameCompare(N1,N2,I,U) (ASSERT(0 == (U)), RtlCompareUnicodeString(N1,N2,I)) #define FspFileNameIsPrefix(N1,N2,I,U) (ASSERT(0 == (U)), RtlPrefixUnicodeString(N1,N2,I)) #endif diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index 0fc41b66..b976a984 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -27,6 +27,11 @@ */ #define MEMFS_NAMED_STREAMS +/* + * Define the MEMFS_NAME_NORMALIZATION macro to include name normalization support. + */ +#define MEMFS_NAME_NORMALIZATION + /* * Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes * a check for the Write buffer to ensure that it is read-only. @@ -500,7 +505,11 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, PVOID *PFileNode, FSP_FSCTL_FILE_INFO *FileInfo) { MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; +#if defined(MEMFS_NAME_NORMALIZATION) + WCHAR FileNameBuf[MAX_PATH]; +#endif MEMFS_FILE_NODE *FileNode; + MEMFS_FILE_NODE *ParentNode; NTSTATUS Result; BOOLEAN Inserted; @@ -511,7 +520,8 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, if (0 != FileNode) return STATUS_OBJECT_NAME_COLLISION; - if (!MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result)) + ParentNode = MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result); + if (0 == ParentNode) return Result; if (MemfsFileNodeMapCount(Memfs->FileNodeMap) >= Memfs->MaxFileNodes) @@ -520,6 +530,31 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, if (AllocationSize > Memfs->MaxFileSize) return STATUS_DISK_FULL; +#if defined(MEMFS_NAME_NORMALIZATION) + if (MemfsFileNodeMapIsCaseInsensitive(Memfs->FileNodeMap)) + { + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + size_t RemainLength, BSlashLength, SuffixLength; + + FspPathSuffix(FileName, &Remain, &Suffix, Root); + assert(0 == MemfsCompareString(Remain, -1, ParentNode->FileName, -1, TRUE)); + FspPathCombine(FileName, Suffix); + + RemainLength = wcslen(ParentNode->FileName); + BSlashLength = 1 < RemainLength; + SuffixLength = wcslen(Suffix); + if (MAX_PATH <= RemainLength + BSlashLength + SuffixLength) + return STATUS_OBJECT_NAME_INVALID; + + memcpy(FileNameBuf, ParentNode->FileName, RemainLength * sizeof(WCHAR)); + memcpy(FileNameBuf + RemainLength, L"\\", BSlashLength * sizeof(WCHAR)); + memcpy(FileNameBuf + RemainLength + BSlashLength, Suffix, (SuffixLength + 1) * sizeof(WCHAR)); + + FileName = FileNameBuf; + } +#endif + Result = MemfsFileNodeCreate(FileName, &FileNode); if (!NT_SUCCESS(Result)) return Result; @@ -567,6 +602,17 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, *PFileNode = FileNode; MemfsFileNodeGetFileInfo(FileNode, FileInfo); +#if defined(MEMFS_NAME_NORMALIZATION) + if (MemfsFileNodeMapIsCaseInsensitive(Memfs->FileNodeMap)) + { + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); + + wcscpy_s(OpenFileInfo->NormalizedName, OpenFileInfo->NormalizedNameSize / sizeof(WCHAR), + FileNode->FileName); + OpenFileInfo->NormalizedNameSize = (UINT16)(wcslen(FileNode->FileName) * sizeof(WCHAR)); + } +#endif + return STATUS_SUCCESS; } @@ -605,6 +651,17 @@ static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, *PFileNode = FileNode; MemfsFileNodeGetFileInfo(FileNode, FileInfo); +#if defined(MEMFS_NAME_NORMALIZATION) + if (MemfsFileNodeMapIsCaseInsensitive(Memfs->FileNodeMap)) + { + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); + + wcscpy_s(OpenFileInfo->NormalizedName, OpenFileInfo->NormalizedNameSize / sizeof(WCHAR), + FileNode->FileName); + OpenFileInfo->NormalizedNameSize = (UINT16)(wcslen(FileNode->FileName) * sizeof(WCHAR)); + } +#endif + return STATUS_SUCCESS; } diff --git a/tst/winfsp-tests/reparse-test.c b/tst/winfsp-tests/reparse-test.c index f658849e..8bcc74ac 100644 --- a/tst/winfsp-tests/reparse-test.c +++ b/tst/winfsp-tests/reparse-test.c @@ -243,11 +243,11 @@ static void reparse_symlink_dotest0(ULONG Flags, PWSTR Prefix, else ASSERT(PNameInfo->FileNameLength == wcslen(FilePath + 1) * sizeof(WCHAR)); if (-1 == Flags) - ASSERT(0 == memcmp(FilePath + 6, PNameInfo->FileName, PNameInfo->FileNameLength)); + ASSERT(0 == mywcscmp(FilePath + 6, -1, PNameInfo->FileName, PNameInfo->FileNameLength / sizeof(WCHAR))); else if (0 == Prefix) - ASSERT(0 == memcmp(L"\\file0", PNameInfo->FileName, PNameInfo->FileNameLength)); + ASSERT(0 == mywcscmp(L"\\file0", -1, PNameInfo->FileName, PNameInfo->FileNameLength / sizeof(WCHAR))); else - ASSERT(0 == memcmp(FilePath + 1, PNameInfo->FileName, PNameInfo->FileNameLength)); + ASSERT(0 == mywcscmp(FilePath + 1, -1, PNameInfo->FileName, PNameInfo->FileNameLength / sizeof(WCHAR))); CloseHandle(Handle); @@ -400,11 +400,11 @@ static BOOL my_namecheck_fn(ULONG Flags, PWSTR Prefix, void *memfs, PWSTR FileNa else ASSERT(PNameInfo->FileNameLength == wcslen(ExpectedPath + 1) * sizeof(WCHAR)); if (-1 == Flags) - ASSERT(0 == memcmp(ExpectedPath + 6, PNameInfo->FileName, PNameInfo->FileNameLength)); + ASSERT(0 == mywcscmp(ExpectedPath + 6, -1, PNameInfo->FileName, PNameInfo->FileNameLength / sizeof(WCHAR))); else if (0 == Prefix) - ASSERT(0 == memcmp(ExpectedPath, PNameInfo->FileName, PNameInfo->FileNameLength)); + ASSERT(0 == mywcscmp(ExpectedPath, -1, PNameInfo->FileName, PNameInfo->FileNameLength / sizeof(WCHAR))); else - ASSERT(0 == memcmp(ExpectedPath + 1, PNameInfo->FileName, PNameInfo->FileNameLength)); + ASSERT(0 == mywcscmp(ExpectedPath + 1, -1, PNameInfo->FileName, PNameInfo->FileNameLength / sizeof(WCHAR))); } CloseHandle(Handle);