diff --git a/Changelog.asciidoc b/Changelog.asciidoc index bd3cc663..d84ece56 100644 --- a/Changelog.asciidoc +++ b/Changelog.asciidoc @@ -1,6 +1,13 @@ = Changelog +v1.4B1 (2018.2 B1):: + +Changes since v1.3: + +* New `Control` file system operation allows sending custom control codes to the file system using the Windows `DeviceIoControl` API. + + v1.3 (2018.1):: Changes since v1.2POST1: diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj index 74c4edc5..2e906314 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj +++ b/build/VStudio/testing/winfsp-tests.vcxproj @@ -182,6 +182,7 @@ + diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters index 0d3b22f1..217f99ff 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj.filters +++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters @@ -88,6 +88,9 @@ Source + + Source + diff --git a/inc/fuse/fuse.h b/inc/fuse/fuse.h index 1b01eadf..dd0c2c03 100644 --- a/inc/fuse/fuse.h +++ b/inc/fuse/fuse.h @@ -87,7 +87,7 @@ struct fuse_operations /* _ */ unsigned int flag_nopath:1; /* _ */ unsigned int flag_utime_omit_ok:1; /* _ */ unsigned int flag_reserved:29; - /* _ */ int (*ioctl)(const char *path, int cmd, void *arg, struct fuse_file_info *fi, + /* S */ int (*ioctl)(const char *path, int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, void *data); /* _ */ int (*poll)(const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp); diff --git a/inc/fuse/winfsp_fuse.h b/inc/fuse/winfsp_fuse.h index 7f1b3ecf..38a8e244 100644 --- a/inc/fuse/winfsp_fuse.h +++ b/inc/fuse/winfsp_fuse.h @@ -53,6 +53,17 @@ extern "C" { #endif #endif +#define FSP_FUSE_DEVICE_TYPE (0x8000 | 'W' | 'F' * 0x100) /* DeviceIoControl -> ioctl */ +#define FSP_FUSE_CTLCODE_FROM_IOCTL(cmd)\ + (FSP_FUSE_DEVICE_TYPE << 16) | (((c) & 0x0fff) << 2) +#define FSP_FUSE_IOCTL(cmd, isiz, osiz) \ + ( \ + (((osiz) != 0) << 31) | \ + (((isiz) != 0) << 30) | \ + (((isiz) | (osiz)) << 16) | \ + (cmd) \ + ) + /* * FUSE uses a number of types (notably: struct stat) that are OS specific. * Furthermore there are sometimes multiple definitions of the same type even diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index b2f52646..570e3e94 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -82,6 +82,8 @@ FSP_FSCTL_STATIC_ASSERT(FSP_FSCTL_VOLUME_NAME_SIZEMAX <= 260 * sizeof(WCHAR), #define FSP_FSCTL_TRANSACT_REQ_TOKEN_HANDLE(T) ((HANDLE)((T) & 0xffffffff)) #define FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(T) ((UINT32)(((T) >> 32) & 0xffffffff)) +#define FSP_FSCTL_DEVICECONTROL_SIZEMAX (4 * 1024) /* must be < FSP_FSCTL_TRANSACT_{REQ,RSP}_SIZEMAX */ + /* marshalling */ #pragma warning(push) #pragma warning(disable:4200) /* zero-sized array in struct/union */ @@ -154,11 +156,13 @@ enum UINT32 AlwaysUseDoubleBuffering:1;\ UINT32 PassQueryDirectoryFileName:1; /* pass FileName during QueryDirectory (GetDirInfoByName) */\ UINT32 FlushAndPurgeOnCleanup:1; /* keeps file off "standby" list */\ - UINT32 KmReservedFlags:1;\ + UINT32 DeviceControl:1; /* support user-mode ioctl handling */\ /* user-mode flags */\ UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */\ UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */\ - UINT32 UmReservedFlags:14;\ + UINT32 UmReservedFlags:6;\ + /* additional kernel-mode flags */\ + UINT32 KmReservedFlags:8;\ WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\ WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; #define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\ @@ -381,6 +385,14 @@ typedef struct UINT16 TargetOnFileSystem; /* the target of the symbolic link is on this file system */ } FileSystemControl; struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT32 IoControlCode; + FSP_FSCTL_TRANSACT_BUF Buffer; + UINT32 OutputLength; + } DeviceControl; + struct { UINT64 UserContext; UINT64 UserContext2; @@ -466,6 +478,10 @@ typedef struct FSP_FSCTL_TRANSACT_BUF Buffer; } FileSystemControl; struct + { + FSP_FSCTL_TRANSACT_BUF Buffer; + } DeviceControl; + struct { FSP_FSCTL_TRANSACT_BUF SecurityDescriptor; } QuerySecurity; diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 6892acc9..c4fcc1f8 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -822,12 +822,41 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE NTSTATUS (*GetDirInfoByName)(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext, PWSTR FileName, FSP_FSCTL_DIR_INFO *DirInfo); + /** + * Process control code. + * + * This function is called when a program uses the DeviceIoControl API. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to be controled. + * @param ControlCode + * The control code for the operation. This code must have a DeviceType with bit + * 0x8000 set and must have a TransferType of METHOD_BUFFERED. + * @param InputBuffer + * Pointer to a buffer that contains the input data. + * @param InputBufferLength + * Input data length. + * @param OutputBuffer + * Pointer to a buffer that will receive the output data. + * @param OutputBufferLength + * Output data length. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes transferred. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*Control)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 ControlCode, + PVOID InputBuffer, ULONG InputBufferLength, + PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred); /* * This ensures that this interface will always contain 64 function pointers. * Please update when changing the interface as it is important for future compatibility. */ - NTSTATUS (*Reserved[39])(); + NTSTATUS (*Reserved[38])(); } FSP_FILE_SYSTEM_INTERFACE; FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()), "FSP_FILE_SYSTEM_INTERFACE must have 64 entries."); @@ -1141,6 +1170,8 @@ FSP_API NTSTATUS FspFileSystemOpQueryDirectory(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpDeviceControl(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_API NTSTATUS FspFileSystemOpQuerySecurity(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_API NTSTATUS FspFileSystemOpSetSecurity(FSP_FILE_SYSTEM *FileSystem, diff --git a/src/dll/fs.c b/src/dll/fs.c index d102a32a..c2facf2e 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -162,6 +162,7 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath, FileSystem->Operations[FspFsctlTransactSetVolumeInformationKind] = FspFileSystemOpSetVolumeInformation; FileSystem->Operations[FspFsctlTransactQueryDirectoryKind] = FspFileSystemOpQueryDirectory; FileSystem->Operations[FspFsctlTransactFileSystemControlKind] = FspFileSystemOpFileSystemControl; + FileSystem->Operations[FspFsctlTransactDeviceControlKind] = FspFileSystemOpDeviceControl; FileSystem->Operations[FspFsctlTransactQuerySecurityKind] = FspFileSystemOpQuerySecurity; FileSystem->Operations[FspFsctlTransactSetSecurityKind] = FspFileSystemOpSetSecurity; FileSystem->Operations[FspFsctlTransactQueryStreamInformationKind] = FspFileSystemOpQueryStreamInformation; diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 37a4e21a..d792ff1e 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1247,6 +1247,30 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem, return Result; } +FSP_API NTSTATUS FspFileSystemOpDeviceControl(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + NTSTATUS Result; + ULONG BytesTransferred; + + if (0 == FileSystem->Interface->Control) + return STATUS_INVALID_DEVICE_REQUEST; + + Result = FileSystem->Interface->Control(FileSystem, + (PVOID)ValOfFileContext(Request->Req.DeviceControl), + Request->Req.DeviceControl.IoControlCode, + Request->Buffer, Request->Req.DeviceControl.Buffer.Size, + Response->Buffer, Request->Req.DeviceControl.OutputLength/* FSD guarantees correct size! */, + &BytesTransferred); + if (!NT_SUCCESS(Result)) + return STATUS_BUFFER_OVERFLOW != Result ? Result : STATUS_BUFFER_TOO_SMALL; + + Response->Size = (UINT16)(sizeof *Response + BytesTransferred); + Response->Rsp.DeviceControl.Buffer.Offset = 0; + Response->Rsp.DeviceControl.Buffer.Size = (UINT16)BytesTransferred; + return STATUS_SUCCESS; +} + FSP_API NTSTATUS FspFileSystemOpQuerySecurity(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index d742642d..d9dbe953 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -649,6 +649,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, opt_data.VolumeParams.ReadOnlyVolume = FALSE; opt_data.VolumeParams.PostCleanupWhenModifiedOnly = TRUE; opt_data.VolumeParams.PassQueryDirectoryFileName = TRUE; + opt_data.VolumeParams.DeviceControl = TRUE; opt_data.VolumeParams.UmFileContextIsUserContext2 = TRUE; if (L'\0' == opt_data.VolumeParams.FileSystemName[0]) memcpy(opt_data.VolumeParams.FileSystemName, L"FUSE", 5 * sizeof(WCHAR)); diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index f764a449..3363a771 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -2125,6 +2125,47 @@ static NTSTATUS fsp_fuse_intf_DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, return STATUS_ACCESS_DENIED; } +static NTSTATUS fsp_fuse_intf_Control(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode, UINT32 ControlCode, + PVOID InputBuffer, ULONG InputBufferLength, + PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred) +{ + struct fuse *f = FileSystem->UserContext; + struct fsp_fuse_file_desc *filedesc = FileNode; + struct fuse_file_info fi; + int cmd; + int err; + + if (0 == f->ops.ioctl) + return STATUS_INVALID_DEVICE_REQUEST; + + if (FSP_FUSE_DEVICE_TYPE != DEVICE_TYPE_FROM_CTL_CODE(ControlCode)) + return STATUS_INVALID_DEVICE_REQUEST; + + if (0 != InputBufferLength && 0 != OutputBufferLength && + InputBufferLength != OutputBufferLength) + return STATUS_INVALID_DEVICE_REQUEST; + + memset(&fi, 0, sizeof fi); + fi.flags = filedesc->OpenFlags; + fi.fh = filedesc->FileHandle; + + /* construct a Linux compatible ioctl code */ + cmd = FSP_FUSE_IOCTL((ControlCode >> 2) & 0xfff, InputBufferLength, OutputBufferLength); + + if (0 == OutputBufferLength) + err = f->ops.ioctl(filedesc->PosixPath, cmd, 0, &fi, 0, InputBuffer); + else + { + if (0 != InputBufferLength) + // OutputBuffer points to Response->Buffer which is FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX long + memcpy(OutputBuffer, InputBuffer, InputBufferLength); + err = f->ops.ioctl(filedesc->PosixPath, cmd, 0, &fi, 0, OutputBuffer); + } + + return fsp_fuse_ntstatus_from_errno(f->env, err); +} + FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf = { fsp_fuse_intf_GetVolumeInfo, @@ -2152,6 +2193,7 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf = fsp_fuse_intf_DeleteReparsePoint, 0, fsp_fuse_intf_GetDirInfoByName, + fsp_fuse_intf_Control, }; /* diff --git a/src/dotnet/FileSystemBase.cs b/src/dotnet/FileSystemBase.cs index e66da75a..e208abd9 100644 --- a/src/dotnet/FileSystemBase.cs +++ b/src/dotnet/FileSystemBase.cs @@ -966,6 +966,49 @@ namespace Fsp FileInfo = default(FileInfo); return STATUS_INVALID_DEVICE_REQUEST; } + /// + /// Processes a control code. + /// + /// + /// This function is called when a program uses the DeviceIoControl API. + /// + /// + /// The file node of the file or directory to be controled. + /// + /// + /// The file descriptor of the file or directory to be controled. + /// + /// + /// The control code for the operation. This code must have a DeviceType with bit + /// 0x8000 set and must have a TransferType of METHOD_BUFFERED. + /// + /// + /// Pointer to a buffer that contains the input data. + /// + /// + /// Input data length. + /// + /// + /// Pointer to a buffer that will receive the output data. + /// + /// + /// Output data length. + /// + /// + /// Receives the actual number of bytes transferred. + /// + /// STATUS_SUCCESS or error code. + public virtual Int32 Control( + Object FileNode, + Object FileDesc, + UInt32 ControlCode, + IntPtr InputBuffer, UInt32 InputBufferLength, + IntPtr OutputBuffer, UInt32 OutputBufferLength, + out UInt32 BytesTransferred) + { + BytesTransferred = default(UInt32); + return STATUS_INVALID_DEVICE_REQUEST; + } /* helpers */ /// diff --git a/src/dotnet/FileSystemHost.cs b/src/dotnet/FileSystemHost.cs index d98ad582..55a14c2c 100644 --- a/src/dotnet/FileSystemHost.cs +++ b/src/dotnet/FileSystemHost.cs @@ -1020,6 +1020,35 @@ namespace Fsp return ExceptionHandler(FileSystem, ex); } } + private static Int32 Control( + IntPtr FileSystemPtr, + ref FullContext FullContext, + UInt32 ControlCode, + IntPtr InputBuffer, UInt32 InputBufferLength, + IntPtr OutputBuffer, UInt32 OutputBufferLength, + out UInt32 PBytesTransferred) + { + FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr); + try + { + Object FileNode, FileDesc; + Api.GetFullContext(ref FullContext, out FileNode, out FileDesc); + return FileSystem.Control( + FileNode, + FileDesc, + ControlCode, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength, + out PBytesTransferred); + } + catch (Exception ex) + { + PBytesTransferred = default(UInt32); + return ExceptionHandler(FileSystem, ex); + } + } static FileSystemHost() { @@ -1048,6 +1077,7 @@ namespace Fsp _FileSystemInterface.DeleteReparsePoint = DeleteReparsePoint; _FileSystemInterface.GetStreamInfo = GetStreamInfo; _FileSystemInterface.GetDirInfoByName = GetDirInfoByName; + _FileSystemInterface.Control = Control; _FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size); Marshal.StructureToPtr(_FileSystemInterface, _FileSystemInterfacePtr, false); diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index c9a7c95b..c81e9b67 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -457,6 +457,14 @@ namespace Fsp.Interop ref FullContext FullContext, [MarshalAs(UnmanagedType.LPWStr)] String FileName, out DirInfo DirInfo); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Int32 Control( + IntPtr FileSystem, + ref FullContext FullContext, + UInt32 ControlCode, + IntPtr InputBuffer, UInt32 InputBufferLength, + IntPtr OutputBuffer, UInt32 OutputBufferLength, + out UInt32 PBytesTransferred); } internal static int Size = IntPtr.Size * 64; @@ -486,7 +494,8 @@ namespace Fsp.Interop internal Proto.DeleteReparsePoint DeleteReparsePoint; internal Proto.GetStreamInfo GetStreamInfo; internal Proto.GetDirInfoByName GetDirInfoByName; - /* NTSTATUS (*Reserved[39])(); */ + internal Proto.Control Control; + /* NTSTATUS (*Reserved[38])(); */ } [SuppressUnmanagedCodeSecurity] diff --git a/src/sys/devctl.c b/src/sys/devctl.c index a076ec63..5fc0e32a 100644 --- a/src/sys/devctl.c +++ b/src/sys/devctl.c @@ -20,20 +20,81 @@ static NTSTATUS FspFsvolDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolDeviceControlComplete; +static FSP_IOP_REQUEST_FINI FspFsvolDeviceControlRequestFini; FSP_DRIVER_DISPATCH FspDeviceControl; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspFsvolDeviceControl) #pragma alloc_text(PAGE, FspFsvolDeviceControlComplete) +#pragma alloc_text(PAGE, FspFsvolDeviceControlRequestFini) #pragma alloc_text(PAGE, FspDeviceControl) #endif +enum +{ + RequestFileNode = 0, +}; + static NTSTATUS FspFsvolDeviceControl( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { PAGED_CODE(); - return STATUS_INVALID_DEVICE_REQUEST; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + PFILE_OBJECT FileObject = IrpSp->FileObject; + ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; + + /* do we support DeviceControl? */ + if (!FsvolDeviceExtension->VolumeParams.DeviceControl) + return STATUS_INVALID_DEVICE_REQUEST; + + /* do not forward IRP's originating in the kernel! */ + if (KernelMode == Irp->RequestorMode) + return STATUS_INVALID_DEVICE_REQUEST; + + /* only allow custom devices and METHOD_BUFFERED */ + if (0 == (DEVICE_TYPE_FROM_CTL_CODE(IoControlCode) & 0x8000) || + METHOD_BUFFERED != METHOD_FROM_CTL_CODE(IoControlCode)) + return STATUS_INVALID_DEVICE_REQUEST; + + /* is this a valid FileObject? */ + if (!FspFileNodeIsValid(FileObject->FsContext)) + return STATUS_INVALID_PARAMETER; + + NTSTATUS Result; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + FSP_FSCTL_TRANSACT_REQ *Request; + + ASSERT(FileNode == FileDesc->FileNode); + + if (FSP_FSCTL_DEVICECONTROL_SIZEMAX < InputBufferLength || + FSP_FSCTL_DEVICECONTROL_SIZEMAX < OutputBufferLength) + return STATUS_INVALID_BUFFER_SIZE; + + Result = FspIopCreateRequestEx(Irp, 0, InputBufferLength, + FspFsvolDeviceControlRequestFini, &Request); + if (!NT_SUCCESS(Result)) + return Result; + + FspFileNodeAcquireShared(FileNode, Full); + + Request->Kind = FspFsctlTransactDeviceControlKind; + Request->Req.DeviceControl.UserContext = FileNode->UserContext; + Request->Req.DeviceControl.UserContext2 = FileDesc->UserContext2; + Request->Req.DeviceControl.IoControlCode = IoControlCode; + Request->Req.DeviceControl.Buffer.Offset = 0; + Request->Req.DeviceControl.Buffer.Size = (UINT16)InputBufferLength; + Request->Req.DeviceControl.OutputLength = OutputBufferLength; + RtlCopyMemory(Request->Buffer, InputBuffer, InputBufferLength); + + FspFileNodeSetOwner(FileNode, Full, Request); + FspIopRequestContext(Request, RequestFileNode) = FileNode; + + return FSP_STATUS_IOQ_POST; } NTSTATUS FspFsvolDeviceControlComplete( @@ -41,12 +102,46 @@ NTSTATUS FspFsvolDeviceControlComplete( { FSP_ENTER_IOC(PAGED_CODE()); + if (!NT_SUCCESS(Response->IoStatus.Status)) + { + Irp->IoStatus.Information = 0; + Result = Response->IoStatus.Status; + FSP_RETURN(); + } + + PVOID OutputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if (Response->Buffer + Response->Rsp.DeviceControl.Buffer.Offset + + Response->Rsp.DeviceControl.Buffer.Size > (PUINT8)Response + Response->Size) + FSP_RETURN(Result = STATUS_INTERNAL_ERROR); + + if (OutputBufferLength >= Response->Rsp.DeviceControl.Buffer.Size) + OutputBufferLength = Response->Rsp.DeviceControl.Buffer.Size; + else + Result = STATUS_BUFFER_OVERFLOW; + + RtlCopyMemory(OutputBuffer, Response->Buffer + Response->Rsp.DeviceControl.Buffer.Offset, + OutputBufferLength); + + Irp->IoStatus.Information = OutputBufferLength; + FSP_LEAVE_IOC( "%s, FileObject=%p", IoctlCodeSym(IrpSp->Parameters.DeviceIoControl.IoControlCode), IrpSp->FileObject); } +static VOID FspFsvolDeviceControlRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]) +{ + PAGED_CODE(); + + FSP_FILE_NODE *FileNode = Context[RequestFileNode]; + + if (0 != FileNode) + FspFileNodeReleaseOwner(FileNode, Full, Request); +} + NTSTATUS FspDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp) { diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index e5006d12..e1ba473e 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -55,6 +55,11 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH, */ #define MEMFS_SLOWIO +/* + * Define the MEMFS_CONTROL macro to include DeviceControl support. + */ +#define MEMFS_CONTROL + /* * 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. @@ -1895,6 +1900,38 @@ static NTSTATUS GetStreamInfo(FSP_FILE_SYSTEM *FileSystem, } #endif +#if defined(MEMFS_CONTROL) +static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 ControlCode, + PVOID InputBuffer, ULONG InputBufferLength, + PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred) +{ + /* MEMFS also supports encryption! See below :) */ + if (CTL_CODE(0x8000 + 'M', 'R', METHOD_BUFFERED, FILE_ANY_ACCESS) == ControlCode) + { + if (OutputBufferLength != InputBufferLength) + return STATUS_INVALID_PARAMETER; + + for (PUINT8 P = (PUINT8)InputBuffer, Q = (PUINT8)OutputBuffer, EndP = P + InputBufferLength; + EndP > P; P++, Q++) + { + if (('A' <= *P && *P <= 'M') || ('a' <= *P && *P <= 'm')) + *Q = *P + 13; + else + if (('N' <= *P && *P <= 'Z') || ('n' <= *P && *P <= 'z')) + *Q = *P - 13; + else + *Q = *P; + } + + *PBytesTransferred = InputBufferLength; + return STATUS_SUCCESS; + } + + return STATUS_INVALID_DEVICE_REQUEST; +} +#endif + static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = { GetVolumeInfo, @@ -1937,6 +1974,11 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = #else 0, #endif +#if defined(MEMFS_CONTROL) + Control, +#else + 0, +#endif }; /* @@ -2028,6 +2070,9 @@ NTSTATUS MemfsCreateFunnel( VolumeParams.PassQueryDirectoryFileName = 1; #endif VolumeParams.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup; +#if defined(MEMFS_CONTROL) + VolumeParams.DeviceControl = 1; +#endif if (0 != VolumePrefix) wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR), diff --git a/tst/winfsp-tests/devctl-test.c b/tst/winfsp-tests/devctl-test.c new file mode 100644 index 00000000..457d40ea --- /dev/null +++ b/tst/winfsp-tests/devctl-test.c @@ -0,0 +1,74 @@ +/** + * @file devctl-test.c + * + * @copyright 2015-2018 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * 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 +#include +#include +#include "memfs.h" + +#include "winfsp-tests.h" + +static void devctl_dotest(ULONG Flags, PWSTR Prefix, PWSTR Drive) +{ + void *memfs = memfs_start(Flags); + + WCHAR FilePath[1024]; + HANDLE Handle; + BOOL Success; + CHAR Buffer[26]; + DWORD BytesTransferred; + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Handle = CreateFileW(FilePath, + GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, 0); + ASSERT(INVALID_HANDLE_VALUE != Handle); + + Success = DeviceIoControl(Handle, + CTL_CODE(0x8000 + 'M', 'R', METHOD_BUFFERED, FILE_ANY_ACCESS), + "ABCDEFghijklmNOPQRStuvwxyz", 26, + Buffer, sizeof Buffer, + &BytesTransferred, + 0); + ASSERT(Success); + + ASSERT(26 == BytesTransferred); + ASSERT(0 == memcmp("NOPQRStuvwxyzABCDEFghijklm", Buffer, BytesTransferred)); + + Success = CloseHandle(Handle); + ASSERT(Success); + + memfs_stop(memfs); +} + +static void devctl_test(void) +{ + if (WinFspDiskTests) + devctl_dotest(MemfsDisk, 0, 0); + if (WinFspNetTests) + devctl_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share"); +} + +void devctl_tests(void) +{ + if (OptExternal) + return; + + TEST(devctl_test); +} diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c index e28b9587..8d4a4437 100644 --- a/tst/winfsp-tests/winfsp-tests.c +++ b/tst/winfsp-tests/winfsp-tests.c @@ -199,6 +199,7 @@ int main(int argc, char *argv[]) TESTSUITE(lock_tests); TESTSUITE(dirctl_tests); TESTSUITE(exec_tests); + TESTSUITE(devctl_tests); TESTSUITE(reparse_tests); TESTSUITE(stream_tests); TESTSUITE(oplock_tests);