#region Disclaimer / License
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Duplicati.Library.Snapshots
{
//The signatures in this file are from http://pinvoke.net
///
/// Various Windows specific calls to support USN
///
internal static class Win32USN
{
public const int ERROR_HANDLE_EOF = 38;
public const int ERROR_INSUFFICIENT_BUFFER = 122;
public const int ERROR_SUCCESS = 0;
public const int ERROR_JOURNAL_ENTRY_DELETED = 1181;
#region Structures
[StructLayout(LayoutKind.Sequential)]
public struct USN_JOURNAL_DATA_V0
{
///
/// The current journal identifier. A journal is assigned a new identifier on creation and can be
/// stamped with a new identifier in the course of its existence.
/// The NTFS file system uses this identifier for an integrity check.
///
public readonly long UsnJournalID;
///
/// The number of first record that can be read from the journal.
///
public readonly long FirstUsn;
///
/// The number of next record to be written to the journal.
///
public readonly long NextUsn;
///
/// The first record that was written into the journal for this journal instance.
/// Enumerating the files or directories on a volume can return a USN lower than this value
/// (in other words, a FirstUsn member value less than the LowestValidUsn member value).
/// If it does, the journal has been stamped with a new identifier since the last USN was written.
/// In this case, LowestValidUsn may indicate a discontinuity in the journal, in which changes
/// to some or all files or directories on the volume may have occurred that are not recorded in the change journal.
///
public readonly long LowestValidUsn;
///
/// The largest USN that the change journal supports. An administrator must delete
/// the change journal as the value of NextUsn approaches this value.
///
public readonly long MaxUsn;
///
/// The target maximum size for the change journal, in bytes. The change journal can grow larger
/// than this value, but it is then truncated at the next NTFS file system checkpoint to less than this value.
///
public readonly long MaximumSize;
///
/// The number of bytes of disk memory added to the end and removed from the beginning of the change
/// journal each time memory is allocated or deallocated. In other words, allocation and deallocation
/// take place in units of this size. An integer multiple of a cluster size is a reasonable value for this member.
///
public readonly long AllocationDelta; //DWORDLONG AllocationDelta
}
[StructLayout(LayoutKind.Sequential)]
public struct READ_USN_JOURNAL_DATA_V0
{
public long StartUsn;
public USNReason ReasonMask;
public uint ReturnOnlyOnClose;
public ulong Timeout;
public ulong BytesToWaitFor;
public long UsnJournalID;
}
[StructLayout(LayoutKind.Sequential)]
public struct USN_RECORD_V2
{
public readonly uint RecordLength;
public readonly ushort MajorVersion;
public readonly ushort MinorVersion;
public readonly ulong FileReferenceNumber;
public readonly ulong ParentFileReferenceNumber;
public readonly long Usn;
public readonly long TimeStamp; // strictly, this is a LARGE_INTEGER in C
public readonly USNReason Reason;
public readonly uint SourceInfo;
public readonly uint SecurityId;
public readonly FileAttributes FileAttributes;
public readonly ushort FileNameLength;
public readonly ushort FileNameOffset;
// immediately after the FileNameOffset comes an array of WCHARs containing the FileName
}
[StructLayout(LayoutKind.Sequential)]
public struct MFT_ENUM_DATA
{
public ulong StartFileReferenceNumber;
public long LowUsn;
public long HighUsn;
}
[StructLayout(LayoutKind.Sequential)]
public struct BY_HANDLE_FILE_INFORMATION
{
public readonly uint FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public readonly uint VolumeSerialNumber;
public readonly uint FileSizeHigh;
public readonly uint FileSizeLow;
public readonly uint NumberOfLinks;
public readonly uint FileIndexHigh;
public readonly uint FileIndexLow;
}
#endregion
#region Enums
[Flags]
public enum EMethod : uint
{
Buffered = 0,
InDirect = 1,
OutDirect = 2,
Neither = 3
}
[Flags]
public enum FsCtl : uint
{
///
/// Causes a journal to be queried when used with DeviceIoControl
///
/// FSCTL_QUERY_USN_JOURNAL
QueryUSNJournal = 0x000900f4,
///
/// Causes a journal to be read when used with DeviceIoControl
///
/// FSCTL_READ_USN_JOURNAL
ReadUSNJournal = 0x000900bb,
///
/// Enumerates the update sequence number (USN) data between two specified boundaries to obtain master file table (MFT) records.
///
/// FSCTL_ENUM_USN_DATA
EnumUSNData = 0x000900b3
}
[Flags]
public enum FileAccess : uint
{
///
///
///
GenericRead = 0x80000000,
///
///
///
GenericWrite = 0x40000000,
///
///
///
GenericExecute = 0x20000000,
///
///
///
GenericAll = 0x10000000
}
[Flags]
public enum FileShare : uint
{
///
///
///
None = 0x00000000,
///
/// Enables subsequent open operations on an object to request read access.
/// Otherwise, other processes cannot open the object if they request read access.
/// If this flag is not specified, but the object has been opened for read access, the function fails.
///
Read = 0x00000001,
///
/// Enables subsequent open operations on an object to request write access.
/// Otherwise, other processes cannot open the object if they request write access.
/// If this flag is not specified, but the object has been opened for write access, the function fails.
///
Write = 0x00000002,
///
/// Enables subsequent open operations on an object to request delete access.
/// Otherwise, other processes cannot open the object if they request delete access.
/// If this flag is not specified, but the object has been opened for delete access, the function fails.
///
Delete = 0x00000004,
///
/// Combination of read and write
///
ReadWrite = Read | Write,
///
/// Combo flag that specifies all access
///
All = None | Read | Write
}
public enum CreationDisposition : uint
{
///
/// Creates a new file. The function fails if a specified file exists.
///
New = 1,
///
/// Creates a new file, always.
/// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes,
/// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
///
CreateAlways = 2,
///
/// Opens a file. The function fails if the file does not exist.
///
OpenExisting = 3,
///
/// Opens a file, always.
/// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
///
OpenAlways = 4,
///
/// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
/// The calling process must open the file with the GENERIC_WRITE access right.
///
TruncateExisting = 5
}
[Flags]
public enum FileAttributes : uint
{
None = 0x0,
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
WriteThrough = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
[Flags]
public enum USNReason : uint
{
USN_REASON_DATA_OVERWRITE = 0x00000001,
USN_REASON_DATA_EXTEND = 0x00000002,
USN_REASON_DATA_TRUNCATION = 0x00000004,
USN_REASON_NAMED_DATA_OVERWRITE = 0x00000010,
USN_REASON_NAMED_DATA_EXTEND = 0x00000020,
USN_REASON_NAMED_DATA_TRUNCATION = 0x00000040,
USN_REASON_FILE_CREATE = 0x00000100,
USN_REASON_FILE_DELETE = 0x00000200,
USN_REASON_EA_CHANGE = 0x00000400,
USN_REASON_SECURITY_CHANGE = 0x00000800,
USN_REASON_RENAME_OLD_NAME = 0x00001000,
USN_REASON_RENAME_NEW_NAME = 0x00002000,
USN_REASON_INDEXABLE_CHANGE = 0x00004000,
USN_REASON_BASIC_INFO_CHANGE = 0x00008000,
USN_REASON_HARD_LINK_CHANGE = 0x00010000,
USN_REASON_COMPRESSION_CHANGE = 0x00020000,
USN_REASON_ENCRYPTION_CHANGE = 0x00040000,
USN_REASON_OBJECT_ID_CHANGE = 0x00080000,
USN_REASON_REPARSE_POINT_CHANGE = 0x00100000,
USN_REASON_STREAM_CHANGE = 0x00200000,
USN_REASON_CLOSE = 0x80000000,
USN_REASON_ANY =
USN_REASON_DATA_OVERWRITE |
USN_REASON_DATA_EXTEND |
USN_REASON_DATA_TRUNCATION |
USN_REASON_NAMED_DATA_OVERWRITE |
USN_REASON_NAMED_DATA_EXTEND |
USN_REASON_NAMED_DATA_TRUNCATION |
USN_REASON_FILE_CREATE |
USN_REASON_FILE_DELETE |
USN_REASON_EA_CHANGE |
USN_REASON_SECURITY_CHANGE |
USN_REASON_RENAME_OLD_NAME |
USN_REASON_RENAME_NEW_NAME |
USN_REASON_INDEXABLE_CHANGE |
USN_REASON_BASIC_INFO_CHANGE |
USN_REASON_HARD_LINK_CHANGE |
USN_REASON_COMPRESSION_CHANGE |
USN_REASON_ENCRYPTION_CHANGE |
USN_REASON_OBJECT_ID_CHANGE |
USN_REASON_REPARSE_POINT_CHANGE |
USN_REASON_STREAM_CHANGE |
USN_REASON_CLOSE
}
#endregion
#region Function calls
///
/// The CreateFile function creates or opens a file, file stream, directory, physical disk, volume, console buffer, tape drive,
/// communications resource, mailslot, or named pipe. The function returns a handle that can be used to access an object.
///
///
/// access to the object, which can be read, write, or both
/// The sharing mode of an object, which can be read, write, both, or none
/// A pointer to a SECURITY_ATTRIBUTES structure that determines whether or not the returned handle can
/// be inherited by child processes. Can be null
/// An action to take on files that exist and do not exist
/// The file attributes and flags.
/// A handle to a template file with the GENERIC_READ access right. The template file supplies file attributes
/// and extended attributes for the file that is being created. This parameter can be null
/// If the function succeeds, the return value is an open handle to a specified file. If a specified file exists before the function
/// all and dwCreationDisposition is CREATE_ALWAYS or OPEN_ALWAYS, a call to GetLastError returns ERROR_ALREADY_EXISTS, even when the function
/// succeeds. If a file does not exist before the call, GetLastError returns 0 (zero).
/// If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.
///
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
FileAccess dwDesiredAccess,
FileShare dwShareMode,
IntPtr SecurityAttributes,
CreationDisposition dwCreationDisposition,
FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile
);
///
/// Sends the dwIoControlCode to the device specified by hDevice.
///
/// Safe handle to the device
/// Device IO Control Code to send
/// Input buffer if required
/// Size of input buffer
/// Output buffer if required
/// Size of output buffer
/// Number of bytes returned in output buffer
/// IntPtr to an 'OVERLAPPED' structure
/// 'true' if successful, otherwise 'false'
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
SafeFileHandle hDevice,
FsCtl IoControlCode,
[In] READ_USN_JOURNAL_DATA_V0 InBuffer,
uint nInBufferSize,
[In] IntPtr OutBuffer,
uint nOutBufferSize,
ref uint pBytesReturned,
[In] IntPtr overlapped //[In] ref System.Threading.NativeOverlapped Overlapped
);
///
/// Sends the dwIoControlCode to the device specified by hDevice.
///
/// Safe handle to the device
/// Device IO Control Code to send
/// Input buffer if required
/// Size of input buffer
/// Output buffer if required
/// Size of output buffer
/// Number of bytes returned in output buffer
/// IntPtr to an 'OVERLAPPED' structure
/// 'true' if successful, otherwise 'false'
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
SafeFileHandle hDevice,
FsCtl IoControlCode,
[In] ref MFT_ENUM_DATA InBuffer,
uint nInBufferSize,
[In] IntPtr OutBuffer,
uint nOutBufferSize,
ref uint pBytesReturned,
[In] IntPtr overlapped //[In] ref System.Threading.NativeOverlapped Overlapped
);
///
/// Sends the dwIoControlCode to the device specified by hDevice.
///
/// Safe handle to the device
/// Device IO Control Code to send
/// Input buffer if required
/// Size of input buffer
/// Output buffer if required
/// Size of output buffer
/// Number of bytes returned in output buffer
/// IntPtr to an 'OVERLAPPED' structure
/// 'true' if successful, otherwise 'false'
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint pBytesReturned,
IntPtr overlapped
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetFileInformationByHandle(
[In] SafeFileHandle hFile,
[Out] out BY_HANDLE_FILE_INFORMATION lpFileInformation
);
#endregion
///
/// Sends the control code to the device specified by handle.
///
///
///
///
///
/// Maximum size of returned buffer
///
public static bool ControlWithInput(
SafeFileHandle handle, FsCtl code,
ref TStructure structure, int bufferSize, out byte[] buffer)
where TStructure : struct
{
uint datalen;
bool controlResult;
buffer = new byte[bufferSize];
var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var structureHandle = GCHandle.Alloc(structure, GCHandleType.Pinned);
var bufferPointer = bufferHandle.AddrOfPinnedObject();
var structurePointer = structureHandle.AddrOfPinnedObject();
try
{
controlResult =
DeviceIoControl(handle, (uint)code,
structurePointer, (uint)Marshal.SizeOf(structure),
bufferPointer, (uint)buffer.Length,
out datalen, IntPtr.Zero);
}
finally
{
structureHandle.Free();
bufferHandle.Free();
}
Array.Resize(ref buffer, (int)datalen);
return controlResult;
}
///
/// Sends the control code to the device specified by handle.
///
///
///
///
///
internal static bool ControlWithOutput(
SafeFileHandle handle, FsCtl code, ref TStructure structure)
where TStructure : struct
{
bool controlResult;
//get our object pointer
var structureHandle = GCHandle.Alloc(structure, GCHandleType.Pinned);
var structurePointer = structureHandle.AddrOfPinnedObject();
try
{
controlResult =
DeviceIoControl(handle, (uint)code,
IntPtr.Zero, 0, structurePointer,
(uint)Marshal.SizeOf(structure),
out _, IntPtr.Zero);
}
finally
{
// always release GH handle
structureHandle.Free();
}
if (controlResult)
{
structure = (TStructure)Marshal.PtrToStructure(structurePointer, typeof(TStructure));
}
return controlResult;
}
}
}