mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 16:33:02 -05:00
367 lines
13 KiB
C
367 lines
13 KiB
C
/**
|
|
* @file sys/fsctl.c
|
|
*
|
|
* @copyright 2015-2016 Bill Zissimopoulos
|
|
*/
|
|
/*
|
|
* This file is part of WinFsp.
|
|
*
|
|
* You can redistribute it and/or modify it under the terms of the
|
|
* GNU Affero General Public License version 3 as published by the
|
|
* Free Software Foundation.
|
|
*
|
|
* Licensees holding a valid commercial license may use this file in
|
|
* accordance with the commercial license agreement provided with the
|
|
* software.
|
|
*/
|
|
|
|
#include <sys/driver.h>
|
|
|
|
static NTSTATUS FspFsctlFileSystemControl(
|
|
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 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 FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
|
|
switch (IrpSp->MinorFunction)
|
|
{
|
|
case IRP_MN_USER_FS_REQUEST:
|
|
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
|
|
{
|
|
case FSP_FSCTL_VOLUME_NAME:
|
|
if (0 != IrpSp->FileObject->FsContext2)
|
|
Result = FspVolumeGetName(FsctlDeviceObject, Irp, IrpSp);
|
|
break;
|
|
case FSP_FSCTL_VOLUME_LIST:
|
|
Result = FspVolumeGetNameList(FsctlDeviceObject, Irp, IrpSp);
|
|
break;
|
|
case FSP_FSCTL_TRANSACT:
|
|
case FSP_FSCTL_TRANSACT_BATCH:
|
|
if (0 != IrpSp->FileObject->FsContext2)
|
|
Result = FspVolumeTransact(FsctlDeviceObject, Irp, IrpSp);
|
|
break;
|
|
case FSP_FSCTL_STOP:
|
|
if (0 != IrpSp->FileObject->FsContext2)
|
|
Result = FspVolumeStop(FsctlDeviceObject, Irp, IrpSp);
|
|
break;
|
|
}
|
|
break;
|
|
case IRP_MN_MOUNT_VOLUME:
|
|
Result = FspVolumeMount(FsctlDeviceObject, Irp, IrpSp);
|
|
break;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static NTSTATUS FspFsvolFileSystemControlReparsePoint(
|
|
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
|
BOOLEAN IsWrite)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
|
|
/* do we support reparse points? */
|
|
if (!FsvolDeviceExtension->VolumeParams.ReparsePoints)
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
/* is this a valid FileObject? */
|
|
if (!FspFileNodeIsValid(FileObject->FsContext))
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
NTSTATUS Result;
|
|
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;
|
|
PREPARSE_DATA_BUFFER ReparseData;
|
|
PWSTR PathBuffer;
|
|
BOOLEAN TargetOnFileSystem = FALSE;
|
|
FSP_FSCTL_TRANSACT_REQ *Request;
|
|
|
|
ASSERT(FileNode == FileDesc->FileNode);
|
|
|
|
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;
|
|
|
|
ReparseData = (PREPARSE_DATA_BUFFER)InputBuffer;
|
|
|
|
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag)
|
|
{
|
|
/* NTFS severely limits symbolic links; we will not do that unless our file system asks */
|
|
if (FsvolDeviceExtension->VolumeParams.ReparsePointsAccessCheck)
|
|
{
|
|
if (KernelMode != Irp->RequestorMode &&
|
|
SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_CREATE_SYMBOLIC_LINK_PRIVILEGE),
|
|
UserMode))
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* determine if target resides on same device as link (convenience for user mode) */
|
|
PathBuffer = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
|
|
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
|
|
if (ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength > 4 * sizeof(WCHAR) &&
|
|
'\\' == PathBuffer[0] &&
|
|
'?' == PathBuffer[1] &&
|
|
'?' == PathBuffer[2] &&
|
|
'\\' == PathBuffer[3])
|
|
{
|
|
UNICODE_STRING TargetDeviceName;
|
|
PFILE_OBJECT TargetDeviceFile;
|
|
PDEVICE_OBJECT TargetDeviceObject;
|
|
|
|
RtlInitEmptyUnicodeString(&TargetDeviceName,
|
|
PathBuffer,
|
|
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength);
|
|
|
|
/* the first path component is assumed to be the device name */
|
|
TargetDeviceName.Length = 4 * sizeof(WCHAR);
|
|
while (TargetDeviceName.Length < TargetDeviceName.MaximumLength &&
|
|
'\\' != TargetDeviceName.Buffer[TargetDeviceName.Length / sizeof(WCHAR)])
|
|
TargetDeviceName.Length += sizeof(WCHAR);
|
|
|
|
Result = IoGetDeviceObjectPointer(&TargetDeviceName,
|
|
FILE_READ_ATTRIBUTES, &TargetDeviceFile, &TargetDeviceObject);
|
|
if (!NT_SUCCESS(Result))
|
|
goto target_check_exit;
|
|
|
|
TargetOnFileSystem = IoGetRelatedDeviceObject(FileObject) == TargetDeviceObject;
|
|
ObDereferenceObject(TargetDeviceFile);
|
|
|
|
target_check_exit:
|
|
;
|
|
}
|
|
}
|
|
|
|
FspFileNodeAcquireExclusive(FileNode, Full);
|
|
}
|
|
else
|
|
{
|
|
if (0 == OutputBuffer || 0 == OutputBufferLength)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/*
|
|
* NtFsControlFile (IopXxxControlFile) will setup Irp->AssociatedIrp.SystemBuffer
|
|
* with enough space for either InputBufferLength or OutputBufferLength. There is
|
|
* no need to call FspBufferUserBuffer ourselves.
|
|
*/
|
|
|
|
FspFileNodeAcquireShared(FileNode, Full);
|
|
}
|
|
|
|
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, IsWrite ? InputBufferLength : 0,
|
|
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);
|
|
|
|
Request->Req.FileSystemControl.TargetOnFileSystem = TargetOnFileSystem;
|
|
}
|
|
|
|
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;
|
|
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 FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
|
|
switch (IrpSp->MinorFunction)
|
|
{
|
|
case IRP_MN_USER_FS_REQUEST:
|
|
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
|
|
{
|
|
case FSP_FSCTL_WORK:
|
|
case FSP_FSCTL_WORK_BEST_EFFORT:
|
|
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;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
NTSTATUS FspFsvolFileSystemControlComplete(
|
|
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response)
|
|
{
|
|
FSP_ENTER_IOC(PAGED_CODE());
|
|
|
|
/* exit now if we do not have a FileObject (FSP_FSCTL_WORK*) */
|
|
if (0 == IrpSp->FileObject)
|
|
FSP_RETURN();
|
|
|
|
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);
|
|
|
|
Result = STATUS_INVALID_DEVICE_REQUEST;
|
|
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;
|
|
}
|
|
break;
|
|
}
|
|
|
|
ASSERT(STATUS_INVALID_DEVICE_REQUEST != Result);
|
|
|
|
FspIopRequestContext(Request, RequestFileNode) = 0;
|
|
FspFileNodeReleaseOwner(FileNode, Full, Request);
|
|
|
|
FSP_LEAVE_IOC(
|
|
"%s%sFileObject=%p",
|
|
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ?
|
|
IoctlCodeSym(IrpSp->Parameters.FileSystemControl.FsControlCode) : "",
|
|
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ? ", " : "",
|
|
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)
|
|
{
|
|
FSP_ENTER_MJ(PAGED_CODE());
|
|
|
|
switch (FspDeviceExtension(DeviceObject)->Kind)
|
|
{
|
|
case FspFsvolDeviceExtensionKind:
|
|
FSP_RETURN(Result = FspFsvolFileSystemControl(DeviceObject, Irp, IrpSp));
|
|
case FspFsctlDeviceExtensionKind:
|
|
FSP_RETURN(Result = FspFsctlFileSystemControl(DeviceObject, Irp, IrpSp));
|
|
default:
|
|
FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
FSP_LEAVE_MJ(
|
|
"%s%sFileObject=%p",
|
|
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ?
|
|
IoctlCodeSym(IrpSp->Parameters.FileSystemControl.FsControlCode) : "",
|
|
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ? ", " : "",
|
|
IrpSp->FileObject);
|
|
}
|