sys, dll: reparse point (symbolic link) support: WIP

This commit is contained in:
Bill Zissimopoulos
2016-07-25 21:27:48 -07:00
parent 380ec074ca
commit a8d76d3e46
8 changed files with 853 additions and 196 deletions

View File

@ -96,6 +96,7 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath,
FileSystem->Operations[FspFsctlTransactQueryVolumeInformationKind] = FspFileSystemOpQueryVolumeInformation;
FileSystem->Operations[FspFsctlTransactSetVolumeInformationKind] = FspFileSystemOpSetVolumeInformation;
FileSystem->Operations[FspFsctlTransactQueryDirectoryKind] = FspFileSystemOpQueryDirectory;
FileSystem->Operations[FspFsctlTransactFileSystemControlKind] = FspFileSystemOpFileSystemControl;
FileSystem->Operations[FspFsctlTransactQuerySecurityKind] = FspFileSystemOpQuerySecurity;
FileSystem->Operations[FspFsctlTransactSetSecurityKind] = FspFileSystemOpSetSecurity;
FileSystem->Interface = Interface;

View File

@ -17,25 +17,38 @@
#include <dll/library.h>
/*
* The FspFileSystemOpEnter/FspFileSystemOpLeave functions guard against
* concurrent accesses. Two concurrency models are provided:
*
* 1. A fine-grained concurrency model where file system NAMESPACE accesses
* are guarded using an exclusive-shared (read-write) lock. File I/O is not
* guarded and concurrent reads/writes/etc. are possible. [Note that the FSD
* will still apply an exclusive-shared lock PER INDIVIDUAL FILE, but it will
* not limit I/O operations for different files.]
*
* The fine-grained concurrency model applies the exclusive-shared lock as
* follows:
* - EXCL: SetVolumeLabel, Create, Cleanup(Delete), SetInformation(Rename)
* - SHRD: GetVolumeInfo, Open, SetInformation(Disposition), ReadDirectory
* - NONE: all other operations
*
* 2. A coarse-grained concurrency model where all file system accesses are
* guarded by a mutually exclusive lock.
*/
/* the following definitions appear to be missing from the user mode headers */
#define SYMLINK_FLAG_RELATIVE 1
typedef struct _REPARSE_DATA_BUFFER
{
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union
{
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct
{
UCHAR DataBuffer[1];
} GenericReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
@ -107,9 +120,34 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
return STATUS_SUCCESS;
}
static inline
NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
IO_STATUS_BLOCK IoStatus;
SIZE_T Size;
if (0 != FileSystem->Interface->ResolveReparsePoints)
{
memset(&IoStatus, 0, sizeof IoStatus);
Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer);
Result = FileSystem->Interface->ResolveReparsePoints(FileSystem,
(PWSTR)Request->Buffer,
!!(Request->Req.Create.CreateOptions & FILE_OPEN_REPARSE_POINT),
&IoStatus,
Response->Buffer,
&Size);
if (STATUS_REPARSE == Result)
Response->IoStatus.Information = (UINT32)IoStatus.Information;
}
return Result;
}
static inline
NTSTATUS FspFileSystemCreateCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN AllowTraverseCheck, PUINT32 PGrantedAccess,
PSECURITY_DESCRIPTOR *PSecurityDescriptor)
{
@ -128,7 +166,9 @@ NTSTATUS FspFileSystemCreateCheck(FSP_FILE_SYSTEM *FileSystem,
(Request->Req.Create.CreateOptions & FILE_DIRECTORY_FILE) ?
FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE,
PGrantedAccess, PSecurityDescriptor);
if (NT_SUCCESS(Result))
if (STATUS_REPARSE == Result)
Result = FspFileSystemResolveReparsePoints(FileSystem, Request, Response);
else if (NT_SUCCESS(Result))
{
*PGrantedAccess = (MAXIMUM_ALLOWED & Request->Req.Create.DesiredAccess) ?
FspGetFileGenericMapping()->GenericAll : Request->Req.Create.DesiredAccess;
@ -139,7 +179,7 @@ NTSTATUS FspFileSystemCreateCheck(FSP_FILE_SYSTEM *FileSystem,
static inline
NTSTATUS FspFileSystemOpenCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN AllowTraverseCheck, PUINT32 PGrantedAccess)
{
NTSTATUS Result;
@ -158,7 +198,9 @@ NTSTATUS FspFileSystemOpenCheck(FSP_FILE_SYSTEM *FileSystem,
Request->Req.Create.DesiredAccess |
((Request->Req.Create.CreateOptions & FILE_DELETE_ON_CLOSE) ? DELETE : 0),
PGrantedAccess);
if (NT_SUCCESS(Result))
if (STATUS_REPARSE == Result)
Result = FspFileSystemResolveReparsePoints(FileSystem, Request, Response);
else if (NT_SUCCESS(Result))
{
if (0 == (Request->Req.Create.DesiredAccess & MAXIMUM_ALLOWED))
*PGrantedAccess &= ~DELETE | (Request->Req.Create.DesiredAccess & DELETE);
@ -169,7 +211,7 @@ NTSTATUS FspFileSystemOpenCheck(FSP_FILE_SYSTEM *FileSystem,
static inline
NTSTATUS FspFileSystemOverwriteCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN AllowTraverseCheck, PUINT32 PGrantedAccess)
{
NTSTATUS Result;
@ -191,7 +233,9 @@ NTSTATUS FspFileSystemOverwriteCheck(FSP_FILE_SYSTEM *FileSystem,
(Supersede ? DELETE : FILE_WRITE_DATA) |
((Request->Req.Create.CreateOptions & FILE_DELETE_ON_CLOSE) ? DELETE : 0),
PGrantedAccess);
if (NT_SUCCESS(Result))
if (STATUS_REPARSE == Result)
Result = FspFileSystemResolveReparsePoints(FileSystem, Request, Response);
else if (NT_SUCCESS(Result))
{
if (0 == (Request->Req.Create.DesiredAccess & MAXIMUM_ALLOWED))
*PGrantedAccess &= ~(DELETE | FILE_WRITE_DATA) |
@ -201,6 +245,26 @@ NTSTATUS FspFileSystemOverwriteCheck(FSP_FILE_SYSTEM *FileSystem,
return Result;
}
static inline
NTSTATUS FspFileSystemOpenTargetDirectoryCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
PUINT32 PGrantedAccess)
{
NTSTATUS Result;
/*
* OpenTargetDirectoryCheck consists of checking the parent directory
* for the desired access.
*/
Result = FspAccessCheck(FileSystem, Request, TRUE, TRUE, Request->Req.Create.DesiredAccess,
PGrantedAccess);
if (STATUS_REPARSE == Result)
Result = FspFileSystemResolveReparsePoints(FileSystem, Request, Response);
return Result;
}
static inline
NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request)
@ -235,7 +299,9 @@ NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem,
CreateRequest->Size = sizeof CreateRequest +
Request->Req.SetInformation.Info.Rename.NewFileName.Size;
CreateRequest->Kind = FspFsctlTransactCreateKind;
CreateRequest->Req.Create.CreateOptions = FILE_DELETE_ON_CLOSE; /* force read-only check! */
CreateRequest->Req.Create.CreateOptions =
FILE_DELETE_ON_CLOSE | /* force read-only check! */
FILE_OPEN_REPARSE_POINT; /* allow rename over reparse point */
CreateRequest->Req.Create.AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken;
CreateRequest->Req.Create.UserMode = TRUE;
CreateRequest->FileName.Offset = 0;
@ -248,6 +314,9 @@ NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem,
MemFree(CreateRequest);
if (STATUS_REPARSE == Result)
Result = STATUS_SUCCESS; /* file system should not return STATUS_REPARSE during rename */
return Result;
}
@ -260,8 +329,9 @@ static NTSTATUS FspFileSystemOpCreate_FileCreate(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode;
FSP_FSCTL_FILE_INFO FileInfo;
Result = FspFileSystemCreateCheck(FileSystem, Request, TRUE, &GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result))
Result = FspFileSystemCreateCheck(FileSystem, Request, Response, TRUE,
&GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
Result = FspCreateSecurityDescriptor(FileSystem, Request, ParentDescriptor, &ObjectDescriptor);
@ -294,8 +364,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOpen(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode;
FSP_FSCTL_FILE_INFO FileInfo;
Result = FspFileSystemOpenCheck(FileSystem, Request, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOpenCheck(FileSystem, Request, Response, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
FileNode = 0;
@ -323,8 +393,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_FILE_INFO FileInfo;
BOOLEAN Create = FALSE;
Result = FspFileSystemOpenCheck(FileSystem, Request, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOpenCheck(FileSystem, Request, Response, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
{
if (STATUS_OBJECT_NAME_NOT_FOUND != Result)
return Result;
@ -348,8 +418,9 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem,
if (Create)
{
Result = FspFileSystemCreateCheck(FileSystem, Request, FALSE, &GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result))
Result = FspFileSystemCreateCheck(FileSystem, Request, Response, FALSE,
&GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
Result = FspCreateSecurityDescriptor(FileSystem, Request, ParentDescriptor, &ObjectDescriptor);
@ -384,8 +455,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwrite(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_FILE_INFO FileInfo;
BOOLEAN Supersede = FILE_SUPERSEDE == ((Request->Req.Create.CreateOptions >> 24) & 0xff);
Result = FspFileSystemOverwriteCheck(FileSystem, Request, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOverwriteCheck(FileSystem, Request, Response, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
FileNode = 0;
@ -413,8 +484,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste
FSP_FSCTL_FILE_INFO FileInfo;
BOOLEAN Create = FALSE;
Result = FspFileSystemOverwriteCheck(FileSystem, Request, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOverwriteCheck(FileSystem, Request, Response, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
{
if (STATUS_OBJECT_NAME_NOT_FOUND != Result)
return Result;
@ -438,8 +509,9 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste
if (Create)
{
Result = FspFileSystemCreateCheck(FileSystem, Request, FALSE, &GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result))
Result = FspFileSystemCreateCheck(FileSystem, Request, Response,
FALSE, &GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
Result = FspCreateSecurityDescriptor(FileSystem, Request, ParentDescriptor, &ObjectDescriptor);
@ -476,9 +548,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenTargetDirectory(FSP_FILE_SYSTEM *F
FSP_FSCTL_FILE_INFO FileInfo;
UINT32 Information;
Result = FspAccessCheck(FileSystem, Request, TRUE, TRUE,
Request->Req.Create.DesiredAccess, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOpenTargetDirectoryCheck(FileSystem, Request, Response, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
FileNode = 0;
@ -819,6 +890,112 @@ FSP_API NTSTATUS FspFileSystemOpQueryDirectory(FSP_FILE_SYSTEM *FileSystem,
return Result;
}
FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
NTSTATUS Result;
PREPARSE_DATA_BUFFER SymlinkReparseData;
SIZE_T Offset, Size;
Result = STATUS_INVALID_DEVICE_REQUEST;
switch (Request->Req.FileSystemControl.FsControlCode)
{
case FSCTL_GET_REPARSE_POINT:
if (0 != FileSystem->Interface->GetReparsePoint)
{
SymlinkReparseData = (PREPARSE_DATA_BUFFER)Response->Buffer;
memset(SymlinkReparseData, 0, sizeof *SymlinkReparseData);
if (1/*!!!: SymbolicLinksOnly*/)
{
Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer) -
sizeof(*SymlinkReparseData);
Size /= 2; /* need space for SubstituteName and PrintName */
Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer,
SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer,
&Size);
if (NT_SUCCESS(Result))
{
Offset = 0;
if (Size > 4 * sizeof(WCHAR) &&
'\\' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[0] &&
'?' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[1] &&
'?' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[2] &&
'\\' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[3])
Offset = 4 * sizeof(WCHAR);
SymlinkReparseData->ReparseTag = IO_REPARSE_TAG_SYMLINK;
SymlinkReparseData->ReparseDataLength = (USHORT)(
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) -
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer) +
Size + Size - Offset);
SymlinkReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset =
0;
SymlinkReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength =
(USHORT)Size;
SymlinkReparseData->SymbolicLinkReparseBuffer.PrintNameOffset =
(USHORT)Size;
SymlinkReparseData->SymbolicLinkReparseBuffer.PrintNameLength =
(USHORT)(Size - Offset);
SymlinkReparseData->SymbolicLinkReparseBuffer.Flags =
Size > 1 * sizeof(WCHAR) &&
'\\' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[0] ?
0 : SYMLINK_FLAG_RELATIVE;
RtlMoveMemory(
SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer + Size / sizeof(WCHAR),
SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer + Offset / sizeof(WCHAR),
(DWORD)(Size - Offset));
Response->Rsp.FileSystemControl.Buffer.Size = (UINT16)Size;
}
}
else
{
Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer);
Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer, SymlinkReparseData, &Size);
if (NT_SUCCESS(Result))
Response->Rsp.FileSystemControl.Buffer.Size = (UINT16)Size;
}
}
break;
case FSCTL_SET_REPARSE_POINT:
if (0 != FileSystem->Interface->SetReparsePoint)
{
SymlinkReparseData = (PREPARSE_DATA_BUFFER)
(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset);
if (1/*!!!: SymbolicLinksOnly*/)
{
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer,
SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer +
SymlinkReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR),
SymlinkReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength);
}
else
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer,
SymlinkReparseData,
Request->Req.FileSystemControl.Buffer.Size);
}
break;
case FSCTL_DELETE_REPARSE_POINT:
if (0 != FileSystem->Interface->SetReparsePoint)
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer, 0, 0);
break;
}
return STATUS_SUCCESS;
}
FSP_API NTSTATUS FspFileSystemOpQuerySecurity(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{

View File

@ -111,18 +111,47 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
break;
}
Result = FspGetSecurityByName(FileSystem, Prefix, 0,
Result = FspGetSecurityByName(FileSystem, Prefix, &FileAttributes,
&SecurityDescriptor, &SecurityDescriptorSize);
FspPathCombine(FileName, Remain);
if (!NT_SUCCESS(Result))
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
{
if (STATUS_OBJECT_NAME_NOT_FOUND == Result)
Result = STATUS_OBJECT_PATH_NOT_FOUND;
goto exit;
}
/*
* We check to see if this is a reparse point and then immediately return
* STATUS_REPARSE. We do this check BEFORE the directory check, because
* contrary to NTFS we want to allow non-directory symlinks to directories.
*
* Note that this effectively turns off traverse checking a path comprised of
* reparse points even when the originating process does not have the Traverse
* privilege. [I am not sure what NTFS does in this case, but POSIX symlinks
* behave similarly.] We will still traverse check the reparsed path when
* the FSD sends it back to us though!
*
* Now if the reparse points are not symlinks (or symlink-like) things
* get even more complicated. Argh! Windows!
*/
if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
Result = STATUS_REPARSE;
goto exit;
}
/*
* Check if this is a directory, otherwise the path is invalid.
*/
if (0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_OBJECT_PATH_NOT_FOUND; /* use STATUS_OBJECT_PATH_INVALID? */
goto exit;
}
if (0 < SecurityDescriptorSize)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, FILE_TRAVERSE,
@ -138,93 +167,115 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
Result = FspGetSecurityByName(FileSystem, FileName, &FileAttributes,
&SecurityDescriptor, &SecurityDescriptorSize);
if (!NT_SUCCESS(Result))
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
goto exit;
if (Request->Req.Create.UserMode && 0 < SecurityDescriptorSize)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, DesiredAccess,
&FspFileGenericMapping, PrivilegeSet, &PrivilegeSetLength, PGrantedAccess, &AccessStatus))
Result = AccessStatus ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
else
Result = FspNtStatusFromWin32(GetLastError());
if (!NT_SUCCESS(Result))
{
/*
* If the desired access includes the DELETE or FILE_READ_ATTRIBUTES
* (or MAXIMUM_ALLOWED) rights we must still check with our parent to
* see if it gives us access (through the FILE_DELETE_CHILD and
* FILE_LIST_DIRECTORY rights).
*
* Does the Windows security model suck? Ermmmm...
*/
if (STATUS_ACCESS_DENIED != Result ||
0 == ((MAXIMUM_ALLOWED | DELETE | FILE_READ_ATTRIBUTES) & DesiredAccess))
goto exit;
Result = FspAccessCheck(FileSystem, Request, TRUE, FALSE,
(MAXIMUM_ALLOWED & DesiredAccess) ? (FILE_DELETE_CHILD | FILE_LIST_DIRECTORY) :
(
((DELETE & DesiredAccess) ? FILE_DELETE_CHILD : 0) |
((FILE_READ_ATTRIBUTES & DesiredAccess) ? FILE_LIST_DIRECTORY : 0)
),
&ParentAccess);
if (!NT_SUCCESS(Result))
{
/* any failure just becomes ACCESS DENIED at this point */
Result = STATUS_ACCESS_DENIED;
goto exit;
}
/* redo the access check but remove the DELETE and/or FILE_READ_ATTRIBUTES rights */
DesiredAccess2 = DesiredAccess & ~(
((FILE_DELETE_CHILD & ParentAccess) ? DELETE : 0) |
((FILE_LIST_DIRECTORY & ParentAccess) ? FILE_READ_ATTRIBUTES : 0));
if (0 != DesiredAccess2)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, DesiredAccess2,
&FspFileGenericMapping, PrivilegeSet, &PrivilegeSetLength, PGrantedAccess, &AccessStatus))
Result = AccessStatus ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
else
/* any failure just becomes ACCESS DENIED at this point */
Result = STATUS_ACCESS_DENIED;
if (!NT_SUCCESS(Result))
goto exit;
}
if (FILE_DELETE_CHILD & ParentAccess)
*PGrantedAccess |= DELETE;
if (FILE_LIST_DIRECTORY & ParentAccess)
*PGrantedAccess |= FILE_READ_ATTRIBUTES;
}
}
if (CheckParentDirectory)
{
/*
* We check to see if this is a reparse point and then immediately return
* STATUS_REPARSE. We do this check BEFORE the directory check, because
* contrary to NTFS we want to allow non-directory symlinks to directories.
*/
if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
Result = STATUS_REPARSE;
goto exit;
}
if (0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_NOT_A_DIRECTORY;
goto exit;
}
}
else
{
/*
* We check to see if this is a reparse point and FILE_OPEN_REPARSE_POINT
* was not specified, in which case we return STATUS_REPARSE.
*/
if (0 != (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
0 == (Request->Req.Create.CreateOptions & FILE_OPEN_REPARSE_POINT))
{
Result = STATUS_REPARSE;
goto exit;
}
if ((Request->Req.Create.CreateOptions & FILE_DIRECTORY_FILE) &&
0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_NOT_A_DIRECTORY;
goto exit;
}
if ((Request->Req.Create.CreateOptions & FILE_NON_DIRECTORY_FILE) &&
0 != (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_FILE_IS_A_DIRECTORY;
goto exit;
}
}
if (Request->Req.Create.UserMode)
{
if (0 < SecurityDescriptorSize)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, DesiredAccess,
&FspFileGenericMapping, PrivilegeSet, &PrivilegeSetLength, PGrantedAccess, &AccessStatus))
Result = AccessStatus ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
else
Result = FspNtStatusFromWin32(GetLastError());
if (!NT_SUCCESS(Result))
{
/*
* If the desired access includes the DELETE or FILE_READ_ATTRIBUTES
* (or MAXIMUM_ALLOWED) rights we must still check with our parent to
* see if it gives us access (through the FILE_DELETE_CHILD and
* FILE_LIST_DIRECTORY rights).
*
* Does the Windows security model suck? Ermmmm...
*/
if (STATUS_ACCESS_DENIED != Result ||
0 == ((MAXIMUM_ALLOWED | DELETE | FILE_READ_ATTRIBUTES) & DesiredAccess))
goto exit;
Result = FspAccessCheck(FileSystem, Request, TRUE, FALSE,
(MAXIMUM_ALLOWED & DesiredAccess) ? (FILE_DELETE_CHILD | FILE_LIST_DIRECTORY) :
(
((DELETE & DesiredAccess) ? FILE_DELETE_CHILD : 0) |
((FILE_READ_ATTRIBUTES & DesiredAccess) ? FILE_LIST_DIRECTORY : 0)
),
&ParentAccess);
if (!NT_SUCCESS(Result))
{
/* any failure just becomes ACCESS DENIED at this point */
Result = STATUS_ACCESS_DENIED;
goto exit;
}
/* redo the access check but remove the DELETE and/or FILE_READ_ATTRIBUTES rights */
DesiredAccess2 = DesiredAccess & ~(
((FILE_DELETE_CHILD & ParentAccess) ? DELETE : 0) |
((FILE_LIST_DIRECTORY & ParentAccess) ? FILE_READ_ATTRIBUTES : 0));
if (0 != DesiredAccess2)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, DesiredAccess2,
&FspFileGenericMapping, PrivilegeSet, &PrivilegeSetLength, PGrantedAccess, &AccessStatus))
Result = AccessStatus ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
else
/* any failure just becomes ACCESS DENIED at this point */
Result = STATUS_ACCESS_DENIED;
if (!NT_SUCCESS(Result))
goto exit;
}
if (FILE_DELETE_CHILD & ParentAccess)
*PGrantedAccess |= DELETE;
if (FILE_LIST_DIRECTORY & ParentAccess)
*PGrantedAccess |= FILE_READ_ATTRIBUTES;
}
}
if (CheckParentDirectory)
{
if (0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_NOT_A_DIRECTORY;
goto exit;
}
}
else
{
if ((Request->Req.Create.CreateOptions & FILE_DIRECTORY_FILE) &&
0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_NOT_A_DIRECTORY;
goto exit;
}
if ((Request->Req.Create.CreateOptions & FILE_NON_DIRECTORY_FILE) &&
0 != (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_FILE_IS_A_DIRECTORY;
goto exit;
}
}
if (0 != (FileAttributes & FILE_ATTRIBUTE_READONLY))
{
if (DesiredAccess &

View File

@ -335,9 +335,12 @@ static NTSTATUS FspFsvolCreateNoLock(
}
/* fix FileAttributes */
ClearFlag(FileAttributes, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY);
ClearFlag(FileAttributes,
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT);
if (CreateOptions & FILE_DIRECTORY_FILE)
SetFlag(FileAttributes, FILE_ATTRIBUTE_DIRECTORY);
if (CreateOptions & FILE_OPEN_REPARSE_POINT)
SetFlag(FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT);
/*
* The new request is associated with our IRP. Go ahead and associate our FileNode/FileDesc
@ -486,6 +489,7 @@ NTSTATUS FspFsvolCreateComplete(
FSP_FILE_NODE *FileNode = FileDesc->FileNode;
FSP_FILE_NODE *OpenedFileNode;
UNICODE_STRING ReparseFileName;
PREPARSE_DATA_BUFFER ReparseData;
if (FspFsctlTransactCreateKind == Request->Kind)
{
@ -498,20 +502,25 @@ NTSTATUS FspFsvolCreateComplete(
}
/* special case STATUS_REPARSE */
if (STATUS_REPARSE == Result)
if (STATUS_REPARSE == Response->IoStatus.Status)
{
ReparseFileName.Buffer =
(PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.FileName.Offset);
ReparseFileName.Length = ReparseFileName.MaximumLength =
Response->Rsp.Create.Reparse.FileName.Size;
Result = STATUS_ACCESS_DENIED;
if (IO_REPARSE == Response->IoStatus.Information)
if (IO_REMOUNT == Response->IoStatus.Information)
{
if (0 == ReparseFileName.Length ||
(PUINT8)ReparseFileName.Buffer + ReparseFileName.Length >
(PUINT8)Response + Response->Size)
FSP_RETURN();
Irp->IoStatus.Information = IO_REMOUNT;
Result = STATUS_REPARSE;
FSP_RETURN();
}
else if (IO_REPARSE == Response->IoStatus.Information)
{
ReparseFileName.Buffer =
(PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.FileName.Offset);
ReparseFileName.Length = ReparseFileName.MaximumLength =
Response->Rsp.Create.Reparse.FileName.Size;
if ((PUINT8)ReparseFileName.Buffer + ReparseFileName.Length >
(PUINT8)Response + Response->Size ||
0 == ReparseFileName.Length)
FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED);
if (ReparseFileName.Length > FileObject->FileName.MaximumLength)
{
@ -539,19 +548,43 @@ NTSTATUS FspFsvolCreateComplete(
* STATUS_REPARSE status returned by the filter. Therefore, it is not
* the responsibility of the filter to free that file object.
*/
}
else
if (IO_REMOUNT == Response->IoStatus.Information)
{
if (0 != ReparseFileName.Length)
FSP_RETURN();
}
else
FSP_RETURN();
Irp->IoStatus.Information = (ULONG_PTR)Response->IoStatus.Information;
Result = Response->IoStatus.Status;
FSP_RETURN();
Irp->IoStatus.Information = IO_REPARSE;
Result = STATUS_REPARSE;
FSP_RETURN();
}
else
{
ReparseData = (PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.Data.Offset);
if ((PUINT8)ReparseData + Response->Rsp.Create.Reparse.Data.Size >
(PUINT8)Response + Response->Size)
{
Result = STATUS_IO_REPARSE_DATA_INVALID;
FSP_RETURN();
}
Result = FsRtlValidateReparsePointBuffer(Response->Rsp.Create.Reparse.Data.Size,
ReparseData);
if (!NT_SUCCESS(Result))
FSP_RETURN();
ASSERT(0 == Irp->Tail.Overlay.AuxiliaryBuffer);
Irp->Tail.Overlay.AuxiliaryBuffer = FspAllocNonPagedExternal(
Response->Rsp.Create.Reparse.Data.Size);
if (0 == Irp->Tail.Overlay.AuxiliaryBuffer)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
FSP_RETURN();
}
RtlCopyMemory(Irp->Tail.Overlay.AuxiliaryBuffer, ReparseData,
Response->Rsp.Create.Reparse.Data.Size);
Irp->IoStatus.Information = ReparseData->ReparseTag;
Result = STATUS_REPARSE;
FSP_RETURN();
}
}
/* fix FileNode->FileName if we were doing SL_OPEN_TARGET_DIRECTORY */

View File

@ -18,21 +18,36 @@
#include <sys/driver.h>
static NTSTATUS FspFsctlFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolFileSystemControlReparsePoint(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
BOOLEAN IsWrite);
static NTSTATUS FspFsvolFileSystemControlReparsePointComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN IsWrite);
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
FSP_IOCMPL_DISPATCH FspFsvolFileSystemControlComplete;
static FSP_IOP_REQUEST_FINI FspFsvolFileSystemControlRequestFini;
FSP_DRIVER_DISPATCH FspFileSystemControl;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspFsctlFileSystemControl)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlReparsePoint)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlReparsePointComplete)
#pragma alloc_text(PAGE, FspFsvolFileSystemControl)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlComplete)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlRequestFini)
#pragma alloc_text(PAGE, FspFileSystemControl)
#endif
enum
{
RequestFileNode = 0,
};
static NTSTATUS FspFsctlFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
@ -44,32 +59,160 @@ static NTSTATUS FspFsctlFileSystemControl(
{
case FSP_FSCTL_VOLUME_NAME:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeGetName(DeviceObject, Irp, IrpSp);
Result = FspVolumeGetName(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_VOLUME_LIST:
Result = FspVolumeGetNameList(DeviceObject, Irp, IrpSp);
Result = FspVolumeGetNameList(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_TRANSACT:
case FSP_FSCTL_TRANSACT_BATCH:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeTransact(DeviceObject, Irp, IrpSp);
Result = FspVolumeTransact(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_STOP:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeStop(DeviceObject, Irp, IrpSp);
Result = FspVolumeStop(FsctlDeviceObject, Irp, IrpSp);
break;
}
break;
case IRP_MN_MOUNT_VOLUME:
Result = FspVolumeMount(DeviceObject, Irp, IrpSp);
Result = FspVolumeMount(FsctlDeviceObject, Irp, IrpSp);
break;
}
return Result;
}
static NTSTATUS FspFsvolFileSystemControlReparsePoint(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
BOOLEAN IsWrite)
{
PAGED_CODE();
/* is this a valid FileObject? */
if (!FspFileNodeIsValid(IrpSp->FileObject->FsContext))
return STATUS_INVALID_DEVICE_REQUEST;
NTSTATUS Result;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
ULONG FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer;
PVOID OutputBuffer = Irp->UserBuffer;
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
ULONG ReparseTag;
FSP_FSCTL_TRANSACT_REQ *Request;
ASSERT(FileNode == FileDesc->FileNode);
/* do we support reparse points? */
if (!FsvolDeviceExtension->VolumeParams.ReparsePoints)
return STATUS_INVALID_DEVICE_REQUEST;
if (IsWrite)
{
if (0 == InputBuffer || 0 == InputBufferLength ||
FSP_FSCTL_TRANSACT_REQ_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_REQ, Buffer) -
(FileNode->FileName.Length + sizeof(WCHAR)) < InputBufferLength)
return STATUS_INVALID_PARAMETER;
Result = FsRtlValidateReparsePointBuffer(InputBufferLength, InputBuffer);
if (!NT_SUCCESS(Result))
return Result;
ReparseTag = ((PREPARSE_DATA_BUFFER)InputBuffer)->ReparseTag;
/* NTFS severely limits symbolic links; we will not do that unless our file system asks */
if (FsvolDeviceExtension->VolumeParams.ReparsePointPrivilegeCheck)
{
if (IO_REPARSE_TAG_SYMLINK == ReparseTag &&
KernelMode != Irp->RequestorMode &&
SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_CREATE_SYMBOLIC_LINK_PRIVILEGE),
UserMode))
return STATUS_ACCESS_DENIED;
}
}
else
{
if (0 != InputBuffer || 0 != InputBufferLength ||
0 == OutputBuffer || 0 == OutputBufferLength)
return STATUS_INVALID_PARAMETER;
Result = FspBufferUserBuffer(Irp, OutputBufferLength, IoWriteAccess);
if (!NT_SUCCESS(Result))
return Result;
}
if (IsWrite)
FspFileNodeAcquireExclusive(FileNode, Full);
else
FspFileNodeAcquireShared(FileNode, Full);
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, InputBufferLength,
FspFsvolFileSystemControlRequestFini, &Request);
if (!NT_SUCCESS(Result))
{
FspFileNodeRelease(FileNode, Full);
return Result;
}
Request->Kind = FspFsctlTransactFileSystemControlKind;
Request->Req.FileSystemControl.UserContext = FileNode->UserContext;
Request->Req.FileSystemControl.UserContext2 = FileDesc->UserContext2;
Request->Req.FileSystemControl.FsControlCode = FsControlCode;
if (IsWrite)
{
Request->Req.FileSystemControl.Buffer.Offset = Request->FileName.Size;
Request->Req.FileSystemControl.Buffer.Size = (UINT16)InputBufferLength;
RtlCopyMemory(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset,
InputBuffer, InputBufferLength);
}
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestFileNode) = FileNode;
return FSP_STATUS_IOQ_POST;
}
static NTSTATUS FspFsvolFileSystemControlReparsePointComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN IsWrite)
{
PAGED_CODE();
if (!IsWrite)
return STATUS_SUCCESS;
NTSTATUS Result;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PVOID OutputBuffer = Irp->AssociatedIrp.SystemBuffer; /* see FspBufferUserBuffer call */
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
if (Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset +
Response->Rsp.FileSystemControl.Buffer.Size > (PUINT8)Response + Response->Size)
return STATUS_IO_REPARSE_DATA_INVALID;
Result = FsRtlValidateReparsePointBuffer(Response->Rsp.FileSystemControl.Buffer.Size,
(PVOID)(Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset));
if (!NT_SUCCESS(Result))
return Result;
if (Response->Rsp.FileSystemControl.Buffer.Size > OutputBufferLength)
return STATUS_BUFFER_TOO_SMALL;
RtlCopyMemory(OutputBuffer, Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset,
Response->Rsp.FileSystemControl.Buffer.Size);
Irp->IoStatus.Information = Response->Rsp.FileSystemControl.Buffer.Size;
return STATUS_SUCCESS;
}
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
@ -81,7 +224,14 @@ static NTSTATUS FspFsvolFileSystemControl(
{
case FSP_FSCTL_WORK:
case FSP_FSCTL_WORK_BEST_EFFORT:
Result = FspVolumeWork(DeviceObject, Irp, IrpSp);
Result = FspVolumeWork(FsvolDeviceObject, Irp, IrpSp);
break;
case FSCTL_GET_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePoint(FsvolDeviceObject, Irp, IrpSp, FALSE);
break;
case FSCTL_SET_REPARSE_POINT:
case FSCTL_DELETE_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePoint(FsvolDeviceObject, Irp, IrpSp, TRUE);
break;
}
break;
@ -95,6 +245,44 @@ NTSTATUS FspFsvolFileSystemControlComplete(
{
FSP_ENTER_IOC(PAGED_CODE());
if (!NT_SUCCESS(Response->IoStatus.Status))
{
Irp->IoStatus.Information = 0;
Result = Response->IoStatus.Status;
FSP_RETURN();
}
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
switch (IrpSp->MinorFunction)
{
case IRP_MN_USER_FS_REQUEST:
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
{
case FSCTL_GET_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePointComplete(Irp, Response, FALSE);
break;
case FSCTL_SET_REPARSE_POINT:
case FSCTL_DELETE_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePointComplete(Irp, Response, TRUE);
break;
default:
ASSERT(0);
Result = STATUS_INVALID_PARAMETER;
break;
}
break;
default:
ASSERT(0);
Result = STATUS_INVALID_PARAMETER;
break;
}
FspIopRequestContext(Request, RequestFileNode) = 0;
FspFileNodeReleaseOwner(FileNode, Full, Request);
FSP_LEAVE_IOC(
"%s%sFileObject=%p",
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ?
@ -103,6 +291,16 @@ NTSTATUS FspFsvolFileSystemControlComplete(
IrpSp->FileObject);
}
static VOID FspFsvolFileSystemControlRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4])
{
PAGED_CODE();
FSP_FILE_NODE *FileNode = Context[RequestFileNode];
if (0 != FileNode)
FspFileNodeReleaseOwner(FileNode, Full, Request);
}
NTSTATUS FspFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp)
{