// Creates a secondary RM under a given 'path' public static void CreateTxFResource(string path) { // Create the directory / ensure it is empty to begin with... Directory.CreateDirectory(path); SafeFileHandle handle = TransactedDirectory.GetDirectoryHandle(path); using (handle) { int bytesReturned = 0; // Issue the IO ctrl asking to create a secondary RM... bool result = NativeMethods.DeviceIoControl( handle, NativeMethods.FsctlTxfsCreateSecondaryRm, IntPtr.Zero, 0, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } } }
// Stops the secondary RM under a given 'path' public static void StopTxFResource(string path) { SafeFileHandle handle = TransactedDirectory.GetDirectoryHandle(path); using (handle) { int bytesReturned = 0; // Issue the IO ctrl asking to stop a secondary RM... bool result = NativeMethods.DeviceIoControl( handle, NativeMethods.FsctlTxfsShutdownRm, IntPtr.Zero, 0, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } } }
// Starts the secondary RM under a given 'path' public static SafeFileHandle StartTxFResource(string path) { // If the user did not give an absolute path, it should become relative to our current directory if (!Path.IsPathRooted(path)) { path = Path.Combine(Directory.GetCurrentDirectory(), path); } // Create the resource if not present to make the OM a bit easier to use if (!Directory.Exists(path)) { CreateTxFResource(path); } // This handle must remain open for the duration of your work inside <path> SafeFileHandle handle = TransactedDirectory.GetDirectoryHandle(path); string txfWorkDir = Path.Combine(path, "TxfLog"); Directory.CreateDirectory(txfWorkDir); const string prepend = "\\??\\"; const string defaultRmLogPath = "TxfLog::TxfLog"; const string defaultTmLogPath = "TxfLog::KtmLog"; string rmLogName = prepend + Path.Combine(txfWorkDir, defaultRmLogPath); UInt16 rmLogNameLength = (UInt16)(rmLogName.Length * 2 + 2); string tmLogName = prepend + Path.Combine(txfWorkDir, defaultTmLogPath); UInt16 tmLogNameLength = (UInt16)(tmLogName.Length * 2 + 2); NativeMethods.TXFS_START_RM_INFORMATION startInfo = new NativeMethods.TXFS_START_RM_INFORMATION(); UInt16 startInfoSize = (UInt16)Marshal.SizeOf(startInfo); startInfo.LogPathLength = rmLogNameLength; startInfo.TmLogPathLength = tmLogNameLength; startInfo.TmLogPathOffset = (UInt32)(startInfoSize + startInfo.LogPathLength); // TODO - Suckage - Wow... I have nothing to say about this... // Convert to an unmanaged buffer and then convert back to a managed byte[] for easy manipulation... IntPtr buffer = Marshal.AllocHGlobal(startInfoSize); Marshal.StructureToPtr(startInfo, buffer, false); byte[] startInfoBuffer = new byte[startInfoSize]; Marshal.Copy(buffer, startInfoBuffer, 0, startInfoSize); // Create the path name buffer UnicodeEncoding unicode = new UnicodeEncoding(); byte[] rawPathBuffer = unicode.GetBytes((rmLogName + "\0\0\0" + tmLogName + '\0').ToCharArray()); // Now make the one true buffer which will get passed to the win32 api... byte[] fullBuffer = new byte[startInfoBuffer.Length + rawPathBuffer.Length - 4]; Array.Copy(startInfoBuffer, fullBuffer, startInfoBuffer.Length - 4); Array.Copy(rawPathBuffer, 0, fullBuffer, startInfoBuffer.Length - 4, rawPathBuffer.Length); // And of course it needs to be unmanaged so the GC doesn't swipe it out from underneath us... IntPtr win32Buffer = Marshal.AllocHGlobal(fullBuffer.Length); Marshal.Copy(fullBuffer, 0, win32Buffer, fullBuffer.Length); int bytesReturned = 0; bool result = NativeMethods.DeviceIoControl( handle, NativeMethods.FsctlTxfsStartRm, win32Buffer, fullBuffer.Length, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // // Now we tell TxF to perform undo processing to make the volume consistent. // TxfRollForwardUndo requires no parameters, other than the rm handle (volume // root dir.) After undo processing is complete, TxF performs any additional // required initialization, and upon return of this routine, is ready to // do work. // // TODO - Correctness - Is 16k really large enough for everyone? FsUtil uses 4k... int inBytesLength = 16 * 1024; IntPtr inBytes = Marshal.AllocHGlobal(inBytesLength); bool needRecovery = true; result = NativeMethods.DeviceIoControl( handle, NativeMethods.FsctlTxfsRollforwardRedo, inBytes, inBytesLength, inBytes, inBytesLength, out bytesReturned, IntPtr.Zero); if (!result) { int status = Marshal.GetLastWin32Error(); if (status == NativeMethods.ErrorRecoveryNotNeeded) { needRecovery = false; } else { // Everything else is an error... throw new System.ComponentModel.Win32Exception(status); } } if (needRecovery) { result = NativeMethods.DeviceIoControl( handle, NativeMethods.FsctlTxfsRollforwardUndo, IntPtr.Zero, 0, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } } return(handle); }