mirror of
https://github.com/winfsp/winfsp.git
synced 2025-06-15 00:02:46 -05:00
sys, dll: reparse point (symbolic link) support: WIP
This commit is contained in:
@ -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;
|
||||
|
263
src/dll/fsop.c
263
src/dll/fsop.c
@ -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)
|
||||
{
|
||||
|
@ -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 &
|
||||
|
Reference in New Issue
Block a user