/*
* dotnet/FileSystemHost.cs
*
* Copyright 2015-2021 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 software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
using System;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using Fsp.Interop;
namespace Fsp
{
///
/// Provides a means to host (mount) a file system.
///
public class FileSystemHost : IDisposable
{
/* ctor/dtor */
///
/// Creates an instance of the FileSystemHost class.
///
/// The file system to host.
public FileSystemHost(FileSystemBase FileSystem)
{
_VolumeParams.Version = (UInt16)Marshal.SizeOf(_VolumeParams);
_VolumeParams.Flags = VolumeParams.UmFileContextIsFullContext;
_FileSystem = FileSystem;
}
~FileSystemHost()
{
Dispose(false);
}
///
/// Unmounts the file system and releases all associated resources.
///
public void Dispose()
{
lock (this)
Dispose(true);
GC.SuppressFinalize(true);
}
protected virtual void Dispose(bool disposing)
{
if (IntPtr.Zero != _FileSystemPtr)
{
Api.FspFileSystemStopDispatcher(_FileSystemPtr);
if (disposing)
try
{
_FileSystem.Unmounted(this);
}
catch (Exception ex)
{
ExceptionHandler(_FileSystem, ex);
}
Api.DisposeUserContext(_FileSystemPtr);
Api.FspFileSystemDelete(_FileSystemPtr);
_FileSystemPtr = IntPtr.Zero;
}
}
/* properties */
///
/// Gets or sets the sector size used by the file system.
///
public UInt16 SectorSize
{
get { return _VolumeParams.SectorSize; }
set { _VolumeParams.SectorSize = value; }
}
///
/// Gets or sets the sectors per allocation unit used by the file system.
///
public UInt16 SectorsPerAllocationUnit
{
get { return _VolumeParams.SectorsPerAllocationUnit; }
set { _VolumeParams.SectorsPerAllocationUnit = value; }
}
///
/// Gets or sets the maximum path component length used by the file system.
///
public UInt16 MaxComponentLength
{
get { return _VolumeParams.MaxComponentLength; }
set { _VolumeParams.MaxComponentLength = value; }
}
///
/// Gets or sets the volume creation time.
///
public UInt64 VolumeCreationTime
{
get { return _VolumeParams.VolumeCreationTime; }
set { _VolumeParams.VolumeCreationTime = value; }
}
///
/// Gets or sets the volume serial number.
///
public UInt32 VolumeSerialNumber
{
get { return _VolumeParams.VolumeSerialNumber; }
set { _VolumeParams.VolumeSerialNumber = value; }
}
///
/// Gets or sets the file information timeout.
///
public UInt32 FileInfoTimeout
{
get { return _VolumeParams.FileInfoTimeout; }
set { _VolumeParams.FileInfoTimeout = value; }
}
///
/// Gets or sets the volume information timeout.
///
public UInt32 VolumeInfoTimeout
{
get
{
return 0 != (_VolumeParams.AdditionalFlags & VolumeParams.VolumeInfoTimeoutValid) ?
_VolumeParams.VolumeInfoTimeout : _VolumeParams.FileInfoTimeout;
}
set
{
_VolumeParams.AdditionalFlags |= VolumeParams.VolumeInfoTimeoutValid;
_VolumeParams.VolumeInfoTimeout = value;
}
}
///
/// Gets or sets the directory information timeout.
///
public UInt32 DirInfoTimeout
{
get
{
return 0 != (_VolumeParams.AdditionalFlags & VolumeParams.DirInfoTimeoutValid) ?
_VolumeParams.DirInfoTimeout : _VolumeParams.FileInfoTimeout;
}
set
{
_VolumeParams.AdditionalFlags |= VolumeParams.DirInfoTimeoutValid;
_VolumeParams.DirInfoTimeout = value;
}
}
///
/// Gets or sets the security information timeout.
///
public UInt32 SecurityTimeout
{
get
{
return 0 != (_VolumeParams.AdditionalFlags & VolumeParams.SecurityTimeoutValid) ?
_VolumeParams.SecurityTimeout : _VolumeParams.FileInfoTimeout;
}
set
{
_VolumeParams.AdditionalFlags |= VolumeParams.SecurityTimeoutValid;
_VolumeParams.SecurityTimeout = value;
}
}
///
/// Gets or sets the stream information timeout.
///
public UInt32 StreamInfoTimeout
{
get
{
return 0 != (_VolumeParams.AdditionalFlags & VolumeParams.StreamInfoTimeoutValid) ?
_VolumeParams.StreamInfoTimeout : _VolumeParams.FileInfoTimeout;
}
set
{
_VolumeParams.AdditionalFlags |= VolumeParams.StreamInfoTimeoutValid;
_VolumeParams.StreamInfoTimeout = value;
}
}
///
/// Gets or sets the EA information timeout.
///
public UInt32 EaTimeout
{
get
{
return 0 != (_VolumeParams.AdditionalFlags & VolumeParams.EaTimeoutValid) ?
_VolumeParams.EaTimeout : _VolumeParams.FileInfoTimeout;
}
set
{
_VolumeParams.AdditionalFlags |= VolumeParams.EaTimeoutValid;
_VolumeParams.EaTimeout = value;
}
}
///
/// Gets or sets a value that determines whether the file system is case sensitive.
///
public Boolean CaseSensitiveSearch
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.CaseSensitiveSearch); }
set { _VolumeParams.Flags |= (value ? VolumeParams.CaseSensitiveSearch : 0); }
}
///
/// Gets or sets a value that determines whether a case insensitive file system
/// preserves case in file names.
///
public Boolean CasePreservedNames
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.CasePreservedNames); }
set { _VolumeParams.Flags |= (value ? VolumeParams.CasePreservedNames : 0); }
}
///
/// Gets or sets a value that determines whether file names support unicode characters.
///
public Boolean UnicodeOnDisk
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.UnicodeOnDisk); }
set { _VolumeParams.Flags |= (value ? VolumeParams.UnicodeOnDisk : 0); }
}
///
/// Gets or sets a value that determines whether the file system supports ACL security.
///
public Boolean PersistentAcls
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.PersistentAcls); }
set { _VolumeParams.Flags |= (value ? VolumeParams.PersistentAcls : 0); }
}
///
/// Gets or sets a value that determines whether the file system supports reparse points.
///
public Boolean ReparsePoints
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.ReparsePoints); }
set { _VolumeParams.Flags |= (value ? VolumeParams.ReparsePoints : 0); }
}
///
/// Gets or sets a value that determines whether the file system allows creation of
/// symbolic links without additional privileges.
///
public Boolean ReparsePointsAccessCheck
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.ReparsePointsAccessCheck); }
set { _VolumeParams.Flags |= (value ? VolumeParams.ReparsePointsAccessCheck : 0); }
}
///
/// Gets or sets a value that determines whether the file system supports named streams.
///
public Boolean NamedStreams
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.NamedStreams); }
set { _VolumeParams.Flags |= (value ? VolumeParams.NamedStreams : 0); }
}
///
/// Gets or sets a value that determines whether the file system supports extended attributes.
///
public Boolean ExtendedAttributes
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.ExtendedAttributes); }
set { _VolumeParams.Flags |= (value ? VolumeParams.ExtendedAttributes : 0); }
}
public Boolean PostCleanupWhenModifiedOnly
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.PostCleanupWhenModifiedOnly); }
set { _VolumeParams.Flags |= (value ? VolumeParams.PostCleanupWhenModifiedOnly : 0); }
}
public Boolean PassQueryDirectoryPattern
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.PassQueryDirectoryPattern); }
set { _VolumeParams.Flags |= (value ? VolumeParams.PassQueryDirectoryPattern : 0); }
}
public Boolean PassQueryDirectoryFileName
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.PassQueryDirectoryFileName); }
set { _VolumeParams.Flags |= (value ? VolumeParams.PassQueryDirectoryFileName : 0); }
}
public Boolean FlushAndPurgeOnCleanup
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.FlushAndPurgeOnCleanup); }
set { _VolumeParams.Flags |= (value ? VolumeParams.FlushAndPurgeOnCleanup : 0); }
}
public Boolean DeviceControl
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.DeviceControl); }
set { _VolumeParams.Flags |= (value ? VolumeParams.DeviceControl : 0); }
}
public Boolean AllowOpenInKernelMode
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.AllowOpenInKernelMode); }
set { _VolumeParams.Flags |= (value ? VolumeParams.AllowOpenInKernelMode : 0); }
}
public Boolean WslFeatures
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.WslFeatures); }
set { _VolumeParams.Flags |= (value ? VolumeParams.WslFeatures : 0); }
}
public Boolean RejectIrpPriorToTransact0
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.RejectIrpPriorToTransact0); }
set { _VolumeParams.Flags |= (value ? VolumeParams.RejectIrpPriorToTransact0 : 0); }
}
///
/// Gets or sets the prefix for a network file system.
///
public String Prefix
{
get { return _VolumeParams.GetPrefix(); }
set { _VolumeParams.SetPrefix(value); }
}
///
/// Gets or sets the file system name.
///
public String FileSystemName
{
get { return _VolumeParams.GetFileSystemName(); }
set { _VolumeParams.SetFileSystemName(value); }
}
/* control */
///
/// Checks whether mounting a file system is possible.
///
///
/// The mount point for the new file system. A value of null means that
/// the file system should use the next available drive letter counting
/// downwards from Z: as its mount point.
///
/// STATUS_SUCCESS or error code.
public Int32 Preflight(String MountPoint)
{
return Api.FspFileSystemPreflight(
_VolumeParams.IsPrefixEmpty() ? Api.ProductName + ".Disk" : Api.ProductName + ".Net",
MountPoint);
}
///
/// Mounts a file system.
///
///
/// The mount point for the new file system. A value of null means that
/// the file system should use the next available drive letter counting
/// downwards from Z: as its mount point.
///
///
/// Security descriptor to use if mounting on (newly created) directory.
/// A value of null means the directory should be created with default
/// security.
///
///
/// If true file system operations are synchronized using an exclusive lock.
///
///
/// A value of 0 disables all debug logging.
/// A value of -1 enables all debug logging.
///
/// STATUS_SUCCESS or error code.
public Int32 Mount(String MountPoint,
Byte[] SecurityDescriptor = null,
Boolean Synchronized = false,
UInt32 DebugLog = 0)
{
return MountEx(MountPoint, 0, SecurityDescriptor, Synchronized, DebugLog);
}
///
/// Mounts a file system.
///
///
/// The mount point for the new file system. A value of null means that
/// the file system should use the next available drive letter counting
/// downwards from Z: as its mount point.
///
///
/// Number of threads to use to service file system requests. A value
/// of 0 means that the default number of threads should be used.
///
///
/// Security descriptor to use if mounting on (newly created) directory.
/// A value of null means the directory should be created with default
/// security.
///
///
/// If true file system operations are synchronized using an exclusive lock.
///
///
/// A value of 0 disables all debug logging.
/// A value of -1 enables all debug logging.
///
/// STATUS_SUCCESS or error code.
public Int32 MountEx(String MountPoint,
UInt32 ThreadCount,
Byte[] SecurityDescriptor = null,
Boolean Synchronized = false,
UInt32 DebugLog = 0)
{
Int32 Result;
try
{
Result = _FileSystem.Init(this);
}
catch (Exception ex)
{
Result = ExceptionHandler(_FileSystem, ex);
}
if (0 > Result)
return Result;
Result = Api.FspFileSystemCreate(
_VolumeParams.IsPrefixEmpty() ? Api.ProductName + ".Disk" : Api.ProductName + ".Net",
ref _VolumeParams, _FileSystemInterfacePtr, out _FileSystemPtr);
if (0 > Result)
return Result;
Api.SetUserContext(_FileSystemPtr, _FileSystem);
Api.FspFileSystemSetOperationGuardStrategy(_FileSystemPtr, Synchronized ?
1/*FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE*/ :
0/*FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE*/);
Api.FspFileSystemSetDebugLog(_FileSystemPtr, DebugLog);
Result = Api.FspFileSystemSetMountPointEx(_FileSystemPtr, MountPoint,
SecurityDescriptor);
if (0 <= Result)
{
try
{
Result = _FileSystem.Mounted(this);
}
catch (Exception ex)
{
Result = ExceptionHandler(_FileSystem, ex);
}
if (0 <= Result)
{
Result = Api.FspFileSystemStartDispatcher(_FileSystemPtr, ThreadCount);
if (0 > Result)
try
{
_FileSystem.Unmounted(this);
}
catch (Exception ex)
{
ExceptionHandler(_FileSystem, ex);
}
}
}
if (0 > Result)
{
Api.DisposeUserContext(_FileSystemPtr);
Api.FspFileSystemDelete(_FileSystemPtr);
_FileSystemPtr = IntPtr.Zero;
}
return Result;
}
///
/// Unmounts the file system and releases all associated resources.
///
public void Unmount()
{
Dispose();
}
///
/// Gets the file system mount point.
///
/// The file system mount point.
public String MountPoint()
{
return IntPtr.Zero != _FileSystemPtr ?
Marshal.PtrToStringUni(Api.FspFileSystemMountPoint(_FileSystemPtr)) : null;
}
public IntPtr FileSystemHandle()
{
return _FileSystemPtr;
}
///
/// Gets the hosted file system.
///
/// The hosted file system.
public FileSystemBase FileSystem()
{
return _FileSystem;
}
///
/// Sets the debug log file to use when debug logging is enabled.
///
///
/// The debug log file name. A value of "-" means standard error output.
///
/// STATUS_SUCCESS or error code.
public static Int32 SetDebugLogFile(String FileName)
{
return Api.SetDebugLogFile(FileName);
}
///
/// Return the installed version of WinFsp.
///
public static Version Version()
{
return Api.GetVersion();
}
///
/// Returns a RequestHint to reference the current operation asynchronously.
///
public UInt64 GetOperationRequestHint()
{
return Api.FspFileSystemGetOperationRequestHint();
}
///
/// Asynchronously complete a Read operation.
///
///
/// A reference to the operation to complete.
///
///
/// STATUS_SUCCESS or error code.
///
///
/// Number of bytes read.
///
public void SendReadResponse(UInt64 RequestHint, Int32 Status, UInt32 BytesTransferred)
{
FspFsctlTransactRsp Response = default(FspFsctlTransactRsp);
Response.Size = 128;
Response.Kind = (UInt32)FspFsctlTransact.ReadKind;
Response.Hint = RequestHint;
Response.IoStatus.Information = BytesTransferred;
Response.IoStatus.Status = (UInt32)Status;
Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response);
}
///
/// Asynchronously complete a Write operation.
///
///
/// A reference to the operation to complete.
///
///
/// STATUS_SUCCESS or error code.
///
///
/// The number of bytes written.
///
///
/// Updated file information.
///
public void SendWriteResponse(UInt64 RequestHint, Int32 Status, UInt32 BytesTransferred, ref FileInfo FileInfo)
{
FspFsctlTransactRsp Response = default(FspFsctlTransactRsp);
Response.Size = 128;
Response.Kind = (UInt32)FspFsctlTransact.WriteKind;
Response.Hint = RequestHint;
Response.IoStatus.Information = BytesTransferred;
Response.IoStatus.Status = (UInt32)Status;
Response.WriteFileInfo = FileInfo;
Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response);
}
///
/// Asynchronously complete a ReadDirectory operation.
///
///
/// A reference to the operation to complete.
///
///
/// STATUS_SUCCESS or error code.
///
///
/// Number of bytes read.
///
public void SendReadDirectoryResponse(UInt64 RequestHint, Int32 Status, UInt32 BytesTransferred)
{
FspFsctlTransactRsp Response = default(FspFsctlTransactRsp);
Response.Size = 128;
Response.Kind = (UInt32)FspFsctlTransact.QueryDirectoryKind;
Response.Hint = RequestHint;
Response.IoStatus.Information = BytesTransferred;
Response.IoStatus.Status = (UInt32)Status;
Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response);
}
///
/// Begin notifying Windows that the file system has file changes.
///
///
///
/// A file system that wishes to notify Windows about file changes must
/// first issue an FspFileSystemBegin call, followed by 0 or more
/// FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
///
/// This operation blocks concurrent file rename operations. File rename
/// operations may interfere with file notification, because a file being
/// notified may also be concurrently renamed. After all file change
/// notifications have been issued, you must make sure to call
/// FspFileSystemNotifyEnd to allow file rename operations to proceed.
///
///
///
/// STATUS_SUCCESS or error code. The error code STATUS_CANT_WAIT means that
/// a file rename operation is currently in progress and the operation must be
/// retried at a later time.
///
public Int32 NotifyBegin(UInt32 Timeout)
{
return Api.FspFileSystemNotifyBegin(_FileSystemPtr, Timeout);
}
///
/// End notifying Windows that the file system has file changes.
///
///
///
/// A file system that wishes to notify Windows about file changes must
/// first issue an FspFileSystemBegin call, followed by 0 or more
/// FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
///
/// This operation allows any blocked file rename operations to proceed.
///
///
/// STATUS_SUCCESS or error code.
public Int32 NotifyEnd()
{
return Api.FspFileSystemNotifyEnd(_FileSystemPtr);
}
///
/// Notify Windows that the file system has file changes.
///
///
///
/// A file system that wishes to notify Windows about file changes must
/// first issue an FspFileSystemBegin call, followed by 0 or more
/// FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
///
/// Note that FspFileSystemNotify requires file names to be normalized. A
/// normalized file name is one that contains the correct case of all characters
/// in the file name.
///
/// For case-sensitive file systems all file names are normalized by definition.
/// For case-insensitive file systems that implement file name normalization,
/// a normalized file name is the one that the file system specifies in the
/// response to Create or Open (see also FspFileSystemGetOpenFileInfo). For
/// case-insensitive file systems that do not implement file name normalization
/// a normalized file name is the upper case version of the file name used
/// to open the file.
///
///
/// STATUS_SUCCESS or error code.
public Int32 Notify(NotifyInfo[] NotifyInfoArray)
{
return Api.FspFileSystemNotify(_FileSystemPtr, NotifyInfoArray);
}
/* FSP_FILE_SYSTEM_INTERFACE */
private static Byte[] ByteBufferNotNull = new Byte[0];
private static Int32 ExceptionHandler(
FileSystemBase FileSystem,
Exception ex)
{
try
{
return FileSystem.ExceptionHandler(ex);
}
catch
{
return unchecked((Int32)0xc00000e9)/*STATUS_UNEXPECTED_IO_ERROR*/;
}
}
private static Int32 GetVolumeInfo(
IntPtr FileSystemPtr,
out VolumeInfo VolumeInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
return FileSystem.GetVolumeInfo(
out VolumeInfo);
}
catch (Exception ex)
{
VolumeInfo = default(VolumeInfo);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 SetVolumeLabel(
IntPtr FileSystemPtr,
String VolumeLabel,
out VolumeInfo VolumeInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
return FileSystem.SetVolumeLabel(
VolumeLabel,
out VolumeInfo);
}
catch (Exception ex)
{
VolumeInfo = default(VolumeInfo);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 GetSecurityByName(
IntPtr FileSystemPtr,
String FileName,
IntPtr PFileAttributes/* or ReparsePointIndex */,
IntPtr SecurityDescriptor,
IntPtr PSecurityDescriptorSize)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
UInt32 FileAttributes;
Byte[] SecurityDescriptorBytes = null;
Int32 Result;
if (IntPtr.Zero != PSecurityDescriptorSize)
SecurityDescriptorBytes = ByteBufferNotNull;
Result = FileSystem.GetSecurityByName(
FileName,
out FileAttributes,
ref SecurityDescriptorBytes);
if (0 <= Result && 260/*STATUS_REPARSE*/ != Result)
{
if (IntPtr.Zero != PFileAttributes)
Marshal.WriteInt32(PFileAttributes, (Int32)FileAttributes);
Result = Api.CopySecurityDescriptor(SecurityDescriptorBytes,
SecurityDescriptor, PSecurityDescriptorSize);
}
return Result;
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 Create(
IntPtr FileSystemPtr,
String FileName,
UInt32 CreateOptions,
UInt32 GrantedAccess,
UInt32 FileAttributes,
IntPtr SecurityDescriptor,
UInt64 AllocationSize,
IntPtr ExtraBuffer,
UInt32 ExtraLength,
Boolean ExtraBufferIsReparsePoint,
ref FullContext FullContext,
ref OpenFileInfo OpenFileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
String NormalizedName;
Int32 Result;
Result = FileSystem.CreateEx(
FileName,
CreateOptions,
GrantedAccess,
FileAttributes,
Api.MakeSecurityDescriptor(SecurityDescriptor),
AllocationSize,
ExtraBuffer,
ExtraLength,
ExtraBufferIsReparsePoint,
out FileNode,
out FileDesc,
out OpenFileInfo.FileInfo,
out NormalizedName);
if (0 <= Result)
{
if (null != NormalizedName)
OpenFileInfo.SetNormalizedName(NormalizedName);
Api.SetFullContext(ref FullContext, FileNode, FileDesc);
}
return Result;
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 Open(
IntPtr FileSystemPtr,
String FileName,
UInt32 CreateOptions,
UInt32 GrantedAccess,
ref FullContext FullContext,
ref OpenFileInfo OpenFileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
String NormalizedName;
Int32 Result;
Result = FileSystem.Open(
FileName,
CreateOptions,
GrantedAccess,
out FileNode,
out FileDesc,
out OpenFileInfo.FileInfo,
out NormalizedName);
if (0 <= Result)
{
if (null != NormalizedName)
OpenFileInfo.SetNormalizedName(NormalizedName);
Api.SetFullContext(ref FullContext, FileNode, FileDesc);
}
return Result;
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 Overwrite(
IntPtr FileSystemPtr,
ref FullContext FullContext,
UInt32 FileAttributes,
Boolean ReplaceFileAttributes,
UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
out FileInfo FileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.OverwriteEx(
FileNode,
FileDesc,
FileAttributes,
ReplaceFileAttributes,
AllocationSize,
Ea,
EaLength,
out FileInfo);
}
catch (Exception ex)
{
FileInfo = default(FileInfo);
return ExceptionHandler(FileSystem, ex);
}
}
private static void Cleanup(
IntPtr FileSystemPtr,
ref FullContext FullContext,
String FileName,
UInt32 Flags)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
FileSystem.Cleanup(
FileNode,
FileDesc,
FileName,
Flags);
}
catch (Exception ex)
{
ExceptionHandler(FileSystem, ex);
}
}
private static void Close(
IntPtr FileSystemPtr,
ref FullContext FullContext)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
FileSystem.Close(
FileNode,
FileDesc);
Api.DisposeFullContext(ref FullContext);
}
catch (Exception ex)
{
ExceptionHandler(FileSystem, ex);
}
}
private static Int32 Read(
IntPtr FileSystemPtr,
ref FullContext FullContext,
IntPtr Buffer,
UInt64 Offset,
UInt32 Length,
out UInt32 PBytesTransferred)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.Read(
FileNode,
FileDesc,
Buffer,
Offset,
Length,
out PBytesTransferred);
}
catch (Exception ex)
{
PBytesTransferred = default(UInt32);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 Write(
IntPtr FileSystemPtr,
ref FullContext FullContext,
IntPtr Buffer,
UInt64 Offset,
UInt32 Length,
Boolean WriteToEndOfFile,
Boolean ConstrainedIo,
out UInt32 PBytesTransferred,
out FileInfo FileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.Write(
FileNode,
FileDesc,
Buffer,
Offset,
Length,
WriteToEndOfFile,
ConstrainedIo,
out PBytesTransferred,
out FileInfo);
}
catch (Exception ex)
{
PBytesTransferred = default(UInt32);
FileInfo = default(FileInfo);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 Flush(
IntPtr FileSystemPtr,
ref FullContext FullContext,
out FileInfo FileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.Flush(
FileNode,
FileDesc,
out FileInfo);
}
catch (Exception ex)
{
FileInfo = default(FileInfo);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 GetFileInfo(
IntPtr FileSystemPtr,
ref FullContext FullContext,
out FileInfo FileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.GetFileInfo(
FileNode,
FileDesc,
out FileInfo);
}
catch (Exception ex)
{
FileInfo = default(FileInfo);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 SetBasicInfo(
IntPtr FileSystemPtr,
ref FullContext FullContext,
UInt32 FileAttributes,
UInt64 CreationTime,
UInt64 LastAccessTime,
UInt64 LastWriteTime,
UInt64 ChangeTime,
out FileInfo FileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.SetBasicInfo(
FileNode,
FileDesc,
FileAttributes,
CreationTime,
LastAccessTime,
LastWriteTime,
ChangeTime,
out FileInfo);
}
catch (Exception ex)
{
FileInfo = default(FileInfo);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 SetFileSize(
IntPtr FileSystemPtr,
ref FullContext FullContext,
UInt64 NewSize,
Boolean SetAllocationSize,
out FileInfo FileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.SetFileSize(
FileNode,
FileDesc,
NewSize,
SetAllocationSize,
out FileInfo);
}
catch (Exception ex)
{
FileInfo = default(FileInfo);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 Rename(
IntPtr FileSystemPtr,
ref FullContext FullContext,
String FileName,
String NewFileName,
Boolean ReplaceIfExists)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.Rename(
FileNode,
FileDesc,
FileName,
NewFileName,
ReplaceIfExists);
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 GetSecurity(
IntPtr FileSystemPtr,
ref FullContext FullContext,
IntPtr SecurityDescriptor,
IntPtr PSecurityDescriptorSize)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Byte[] SecurityDescriptorBytes;
Int32 Result;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
SecurityDescriptorBytes = ByteBufferNotNull;
Result = FileSystem.GetSecurity(
FileNode,
FileDesc,
ref SecurityDescriptorBytes);
if (0 <= Result)
Result = Api.CopySecurityDescriptor(SecurityDescriptorBytes,
SecurityDescriptor, PSecurityDescriptorSize);
return Result;
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 SetSecurity(
IntPtr FileSystemPtr,
ref FullContext FullContext,
UInt32 SecurityInformation,
IntPtr ModificationDescriptor)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
AccessControlSections Sections;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
Sections = AccessControlSections.None;
if (0 != (SecurityInformation & 1/*OWNER_SECURITY_INFORMATION*/))
Sections |= AccessControlSections.Owner;
if (0 != (SecurityInformation & 2/*GROUP_SECURITY_INFORMATION*/))
Sections |= AccessControlSections.Group;
if (0 != (SecurityInformation & 4/*DACL_SECURITY_INFORMATION*/))
Sections |= AccessControlSections.Access;
if (0 != (SecurityInformation & 8/*SACL_SECURITY_INFORMATION*/))
Sections |= AccessControlSections.Audit;
return FileSystem.SetSecurity(
FileNode,
FileDesc,
Sections,
Api.MakeSecurityDescriptor(ModificationDescriptor));
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 ReadDirectory(
IntPtr FileSystemPtr,
ref FullContext FullContext,
String Pattern,
String Marker,
IntPtr Buffer,
UInt32 Length,
out UInt32 PBytesTransferred)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.ReadDirectory(
FileNode,
FileDesc,
Pattern,
Marker,
Buffer,
Length,
out PBytesTransferred);
}
catch (Exception ex)
{
PBytesTransferred = default(UInt32);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 ResolveReparsePoints(
IntPtr FileSystemPtr,
String FileName,
UInt32 ReparsePointIndex,
Boolean ResolveLastPathComponent,
out IoStatusBlock PIoStatus,
IntPtr Buffer,
IntPtr PSize)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
return FileSystem.ResolveReparsePoints(
FileName,
ReparsePointIndex,
ResolveLastPathComponent,
out PIoStatus,
Buffer,
PSize);
}
catch (Exception ex)
{
PIoStatus = default(IoStatusBlock);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 GetReparsePoint(
IntPtr FileSystemPtr,
ref FullContext FullContext,
String FileName,
IntPtr Buffer,
IntPtr PSize)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Byte[] ReparseData;
Object FileNode, FileDesc;
Int32 Result;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
ReparseData = null;
Result = FileSystem.GetReparsePoint(
FileNode,
FileDesc,
FileName,
ref ReparseData);
if (0 <= Result)
Result = Api.CopyReparsePoint(ReparseData, Buffer, PSize);
return Result;
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 SetReparsePoint(
IntPtr FileSystemPtr,
ref FullContext FullContext,
String FileName,
IntPtr Buffer,
UIntPtr Size)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.SetReparsePoint(
FileNode,
FileDesc,
FileName,
Api.MakeReparsePoint(Buffer, Size));
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 DeleteReparsePoint(
IntPtr FileSystemPtr,
ref FullContext FullContext,
String FileName,
IntPtr Buffer,
UIntPtr Size)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.DeleteReparsePoint(
FileNode,
FileDesc,
FileName,
Api.MakeReparsePoint(Buffer, Size));
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 GetStreamInfo(
IntPtr FileSystemPtr,
ref FullContext FullContext,
IntPtr Buffer,
UInt32 Length,
out UInt32 PBytesTransferred)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.GetStreamInfo(
FileNode,
FileDesc,
Buffer,
Length,
out PBytesTransferred);
}
catch (Exception ex)
{
PBytesTransferred = default(UInt32);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 GetDirInfoByName(
IntPtr FileSystemPtr,
ref FullContext FullContext,
String FileName,
out DirInfo DirInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
String NormalizedName;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
DirInfo = default(DirInfo);
Int32 Result = FileSystem.GetDirInfoByName(
FileNode,
FileDesc,
FileName,
out NormalizedName,
out DirInfo.FileInfo);
DirInfo.SetFileNameBuf(NormalizedName);
return Result;
}
catch (Exception ex)
{
DirInfo = default(DirInfo);
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);
}
}
private static Int32 SetDelete(
IntPtr FileSystemPtr,
ref FullContext FullContext,
String FileName,
Boolean DeleteFile)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.SetDelete(
FileNode,
FileDesc,
FileName,
DeleteFile);
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 GetEa(
IntPtr FileSystemPtr,
ref FullContext FullContext,
IntPtr Ea,
UInt32 EaLength,
out UInt32 PBytesTransferred)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.GetEa(
FileNode,
FileDesc,
Ea,
EaLength,
out PBytesTransferred);
}
catch (Exception ex)
{
PBytesTransferred = default(UInt32);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 SetEa(
IntPtr FileSystemPtr,
ref FullContext FullContext,
IntPtr Ea,
UInt32 EaLength,
out FileInfo FileInfo)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.SetEa(
FileNode,
FileDesc,
Ea,
EaLength,
out FileInfo);
}
catch (Exception ex)
{
FileInfo = default(FileInfo);
return ExceptionHandler(FileSystem, ex);
}
}
static FileSystemHost()
{
_FileSystemInterface.GetVolumeInfo = GetVolumeInfo;
_FileSystemInterface.SetVolumeLabel = SetVolumeLabel;
_FileSystemInterface.GetSecurityByName = GetSecurityByName;
_FileSystemInterface.CreateEx = Create;
_FileSystemInterface.Open = Open;
_FileSystemInterface.OverwriteEx = Overwrite;
_FileSystemInterface.Cleanup = Cleanup;
_FileSystemInterface.Close = Close;
_FileSystemInterface.Read = Read;
_FileSystemInterface.Write = Write;
_FileSystemInterface.Flush = Flush;
_FileSystemInterface.GetFileInfo = GetFileInfo;
_FileSystemInterface.SetBasicInfo = SetBasicInfo;
_FileSystemInterface.SetFileSize = SetFileSize;
_FileSystemInterface.Rename = Rename;
_FileSystemInterface.GetSecurity = GetSecurity;
_FileSystemInterface.SetSecurity = SetSecurity;
_FileSystemInterface.ReadDirectory = ReadDirectory;
_FileSystemInterface.ResolveReparsePoints = ResolveReparsePoints;
_FileSystemInterface.GetReparsePoint = GetReparsePoint;
_FileSystemInterface.SetReparsePoint = SetReparsePoint;
_FileSystemInterface.DeleteReparsePoint = DeleteReparsePoint;
_FileSystemInterface.GetStreamInfo = GetStreamInfo;
_FileSystemInterface.GetDirInfoByName = GetDirInfoByName;
_FileSystemInterface.Control = Control;
_FileSystemInterface.SetDelete = SetDelete;
_FileSystemInterface.GetEa = GetEa;
_FileSystemInterface.SetEa = SetEa;
_FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size);
/* Marshal.AllocHGlobal does not zero memory; we must do it ourselves! */
for (int Offset = 0; FileSystemInterface.Size > Offset; Offset += IntPtr.Size)
Marshal.WriteIntPtr(_FileSystemInterfacePtr, Offset, IntPtr.Zero);
Marshal.StructureToPtr(_FileSystemInterface, _FileSystemInterfacePtr, false);
}
private static FileSystemInterface _FileSystemInterface;
private static IntPtr _FileSystemInterfacePtr;
private VolumeParams _VolumeParams;
private FileSystemBase _FileSystem;
private IntPtr _FileSystemPtr;
}
}