From 3e0f2316a77d2b7fa9668b2624cf6d2dabb2a43e Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 12 Oct 2016 11:18:55 -0700 Subject: [PATCH] dll: special case STATUS_OBJECT_NAME_NOT_FOUND and STATUS_OBJECT_NAME_COLLISION to handle open/create via symlink tst: memfs: support reparse point functionality over a named stream and fix status return on create collision over directory --- ext/test | 2 +- src/dll/fsop.c | 110 ++++++++++++++++++++++++++++++++++++++++---- src/dll/library.h | 13 ++++++ src/dll/security.c | 13 ------ tst/memfs/memfs.cpp | 28 ++++++++++- 5 files changed, 141 insertions(+), 25 deletions(-) diff --git a/ext/test b/ext/test index 0059e0a2..2d629a42 160000 --- a/ext/test +++ b/ext/test @@ -1 +1 @@ -Subproject commit 0059e0a24a5ddb612e112dedfa5aaf81d6b522e3 +Subproject commit 2d629a427ff1d0f04d2117a414cd7b385ae8b95f diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 6e988e13..2605ddc3 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -602,9 +602,84 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenTargetDirectory(FSP_FILE_SYSTEM *F return STATUS_SUCCESS; } +static NTSTATUS FspFileSystemOpCreate_NotFoundCheck(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + /* + * This handles an Open of a named stream done via a symlink. The file system + * has returned STATUS_OBJECT_NAME_NOT_FOUND, but we check to see if the main + * file is a reparse point that can be resolved. + */ + + NTSTATUS Result; + UINT32 FileAttributes; + + if (0 == FileSystem->Interface->GetSecurityByName || + 0 == FileSystem->Interface->ResolveReparsePoints) + return STATUS_OBJECT_NAME_NOT_FOUND; + + if (!Request->Req.Create.NamedStream || + (Request->Req.Create.CreateOptions & FILE_OPEN_REPARSE_POINT)) + return STATUS_OBJECT_NAME_NOT_FOUND; + + ((PWSTR)Request->Buffer)[Request->Req.Create.NamedStream / sizeof(WCHAR)] = L'\0'; + Result = FileSystem->Interface->GetSecurityByName( + FileSystem, (PWSTR)Request->Buffer, &FileAttributes, 0, 0); + ((PWSTR)Request->Buffer)[Request->Req.Create.NamedStream / sizeof(WCHAR)] = L':'; + + if (STATUS_SUCCESS != Result || + FILE_ATTRIBUTE_REPARSE_POINT != (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + return STATUS_OBJECT_NAME_NOT_FOUND; + + FileAttributes = FspPathSuffixIndex((PWSTR)Request->Buffer); + Result = FspFileSystemCallResolveReparsePoints( + FileSystem, Request, Response, FileAttributes); + if (STATUS_REPARSE != Result) + return STATUS_OBJECT_NAME_NOT_FOUND; + + return STATUS_REPARSE; +} + +static NTSTATUS FspFileSystemOpCreate_CollisionCheck(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + /* + * This handles a Create done via a symlink. The file system has returned + * STATUS_OBJECT_NAME_COLLISION, but we check to see if the collision is + * due to a reparse point that can be resolved. + */ + + NTSTATUS Result; + UINT32 FileAttributes; + + if (0 == FileSystem->Interface->GetSecurityByName || + 0 == FileSystem->Interface->ResolveReparsePoints) + return STATUS_OBJECT_NAME_COLLISION; + + if (Request->Req.Create.CreateOptions & FILE_OPEN_REPARSE_POINT) + return STATUS_OBJECT_NAME_COLLISION; + + Result = FileSystem->Interface->GetSecurityByName( + FileSystem, (PWSTR)Request->Buffer, &FileAttributes, 0, 0); + if (STATUS_SUCCESS != Result || + FILE_ATTRIBUTE_REPARSE_POINT != + (FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))) + return STATUS_OBJECT_NAME_COLLISION; + + FileAttributes = FspPathSuffixIndex((PWSTR)Request->Buffer); + Result = FspFileSystemCallResolveReparsePoints( + FileSystem, Request, Response, FileAttributes); + if (STATUS_REPARSE != Result) + return STATUS_OBJECT_NAME_COLLISION; + + return STATUS_REPARSE; +} + FSP_API NTSTATUS FspFileSystemOpCreate(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { + NTSTATUS Result; + if (0 == FileSystem->Interface->Create || 0 == FileSystem->Interface->Open || 0 == FileSystem->Interface->Overwrite) @@ -616,19 +691,32 @@ FSP_API NTSTATUS FspFileSystemOpCreate(FSP_FILE_SYSTEM *FileSystem, switch ((Request->Req.Create.CreateOptions >> 24) & 0xff) { case FILE_CREATE: - return FspFileSystemOpCreate_FileCreate(FileSystem, Request, Response); + Result = FspFileSystemOpCreate_FileCreate(FileSystem, Request, Response); + break; case FILE_OPEN: - return FspFileSystemOpCreate_FileOpen(FileSystem, Request, Response); + Result = FspFileSystemOpCreate_FileOpen(FileSystem, Request, Response); + break; case FILE_OPEN_IF: - return FspFileSystemOpCreate_FileOpenIf(FileSystem, Request, Response); + Result = FspFileSystemOpCreate_FileOpenIf(FileSystem, Request, Response); + break; case FILE_OVERWRITE: case FILE_SUPERSEDE: - return FspFileSystemOpCreate_FileOverwrite(FileSystem, Request, Response); + Result = FspFileSystemOpCreate_FileOverwrite(FileSystem, Request, Response); + break; case FILE_OVERWRITE_IF: - return FspFileSystemOpCreate_FileOverwriteIf(FileSystem, Request, Response); + Result = FspFileSystemOpCreate_FileOverwriteIf(FileSystem, Request, Response); + break; default: - return STATUS_INVALID_PARAMETER; + Result = STATUS_INVALID_PARAMETER; + break; } + + if (STATUS_OBJECT_NAME_NOT_FOUND == Result) + Result = FspFileSystemOpCreate_NotFoundCheck(FileSystem, Request, Response); + else if (STATUS_OBJECT_NAME_COLLISION == Result) + Result = FspFileSystemOpCreate_CollisionCheck(FileSystem, Request, Response); + + return Result; } FSP_API NTSTATUS FspFileSystemOpOverwrite(FSP_FILE_SYSTEM *FileSystem, @@ -1093,7 +1181,7 @@ FSP_API BOOLEAN FspFileSystemFindReparsePoint(FSP_FILE_SYSTEM *FileSystem, LastPathComponent = RemainderPath; while (L'\\' != *RemainderPath) { - if (L'\0' == *RemainderPath) + if (L'\0' == *RemainderPath || L':' == *RemainderPath) return FALSE; RemainderPath++; } @@ -1161,7 +1249,7 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePointsInternal(FSP_FILE_SYSTEM *File LastPathComponent = RemainderPath; while (L'\\' != *RemainderPath) { - if (L'\0' == *RemainderPath) + if (L'\0' == *RemainderPath || L':' == *RemainderPath) { if (!ResolveLastPathComponent) goto symlink_exit; @@ -1217,16 +1305,18 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePointsInternal(FSP_FILE_SYSTEM *File RemainderChar = *RemainderPath; *RemainderPath = L'\0'; ReparseDataSize = ReparseDataSize0; - Result = GetReparsePointByName(FileSystem, Context, TargetPath, '\0' != RemainderChar, + Result = GetReparsePointByName(FileSystem, Context, TargetPath, L'\\' == RemainderChar, ReparseData, &ReparseDataSize); *RemainderPath = RemainderChar; if (STATUS_NOT_A_REPARSE_POINT == Result) /* it was not a reparse point; continue */ continue; + else if (STATUS_OBJECT_NAME_NOT_FOUND == Result && L'\\' != RemainderChar) + goto symlink_exit; else if (!NT_SUCCESS(Result)) { - if (STATUS_OBJECT_NAME_NOT_FOUND != Result || '\0' != RemainderChar) + if (STATUS_OBJECT_NAME_NOT_FOUND != Result) Result = STATUS_OBJECT_PATH_NOT_FOUND; return Result; } diff --git a/src/dll/library.h b/src/dll/library.h index bab577a1..ec921c3d 100644 --- a/src/dll/library.h +++ b/src/dll/library.h @@ -53,4 +53,17 @@ PWSTR FspDiagIdent(VOID); BOOL WINAPI FspServiceConsoleCtrlHandler(DWORD CtrlType); +static inline ULONG FspPathSuffixIndex(PWSTR FileName) +{ + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + ULONG Result; + + FspPathSuffix(FileName, &Remain, &Suffix, Root); + Result = Remain == Root ? 0 : (ULONG)(Suffix - Remain); + FspPathCombine(FileName, Suffix); + + return Result; +} + #endif diff --git a/src/dll/security.c b/src/dll/security.c index 0498410f..6fbe5d3e 100644 --- a/src/dll/security.c +++ b/src/dll/security.c @@ -48,19 +48,6 @@ static NTSTATUS FspGetSecurityByName(FSP_FILE_SYSTEM *FileSystem, } } -static inline ULONG FspPathSuffixIndex(PWSTR FileName) -{ - WCHAR Root[2] = L"\\"; - PWSTR Remain, Suffix; - ULONG Result; - - FspPathSuffix(FileName, &Remain, &Suffix, Root); - Result = Remain == Root ? 0 : (ULONG)(Suffix - Remain); - FspPathCombine(FileName, Suffix); - - return Result; -} - FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, BOOLEAN CheckParentOrMain, BOOLEAN AllowTraverseCheck, diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index 67edfcd0..70cbf7ec 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -470,7 +470,13 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName); if (0 != FileNode) - return STATUS_OBJECT_NAME_COLLISION; + { + if ((CreateOptions & FILE_NON_DIRECTORY_FILE) && + 0 != (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + return STATUS_FILE_IS_A_DIRECTORY; + else + return STATUS_OBJECT_NAME_COLLISION; + } if (!MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result)) return Result; @@ -1107,6 +1113,11 @@ static NTSTATUS GetReparsePointByName( MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; MEMFS_FILE_NODE *FileNode; +#if defined(MEMFS_NAMED_STREAMS) + /* GetReparsePointByName will never receive a named stream */ + assert(0 == wcschr(FileName, L':')); +#endif + FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName); if (0 == FileNode) return STATUS_OBJECT_NAME_NOT_FOUND; @@ -1133,6 +1144,11 @@ static NTSTATUS GetReparsePoint(FSP_FILE_SYSTEM *FileSystem, { MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) return STATUS_NOT_A_REPARSE_POINT; @@ -1155,6 +1171,11 @@ static NTSTATUS SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID ReparseData; NTSTATUS Result; +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) return STATUS_DIRECTORY_NOT_EMPTY; @@ -1189,6 +1210,11 @@ static NTSTATUS DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; NTSTATUS Result; +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + if (0 != FileNode->ReparseData) { Result = FspFileSystemCanReplaceReparsePoint(