/* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.AccessControl;
using System.Security.Permissions;
using System.Transactions;
namespace Alphaleonis.Win32.Filesystem
{
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("79427A2B-F895-40e0-BE79-B57DC82ED231")]
[SuppressUnmanagedCodeSecurity]
internal interface IKernelTransaction
{
void GetHandle([Out] out SafeKernelTransactionHandle handle);
}
/// A KTM transaction object for use with the transacted operations in
public sealed class KernelTransaction : MarshalByRefObject, IDisposable
{
/// Initializes a new instance of the class, internally using the specified .
/// This method allows the usage of methods accepting a with an instance of .
///
/// The transaction to use for any transactional operations.
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
[SecurityCritical]
public KernelTransaction(Transaction transaction)
{
((IKernelTransaction) TransactionInterop.GetDtcTransaction(transaction)).GetHandle(out _hTrans);
}
/// Initializes a new instance of the class with a default security descriptor, infinite timeout and no description.
[SecurityCritical]
public KernelTransaction()
: this(0, null)
{
}
/// Initializes a new instance of the class with a default security descriptor.
/// The time, in milliseconds, when the transaction will be aborted if it has not already reached the prepared state.
/// A user-readable description of the transaction. This parameter may be .
[SecurityCritical]
public KernelTransaction(int timeout, string description)
: this(null, timeout, description)
{
}
/// Initializes a new instance of the class.
/// The security descriptor.
/// The time, in milliseconds, when the transaction will be aborted if it has not already reached the prepared state.
/// Specify 0 to provide an infinite timeout.
/// A user-readable description of the transaction. This parameter may be .
[SecurityCritical]
public KernelTransaction(ObjectSecurity securityDescriptor, int timeout, string description)
{
if (!NativeMethods.IsAtLeastWindowsVista)
throw new PlatformNotSupportedException(Resources.Requires_Windows_Vista_Or_Higher);
using (var securityAttributes = new Security.NativeMethods.SecurityAttributes(securityDescriptor))
{
_hTrans = NativeMethods.CreateTransaction(securityAttributes, IntPtr.Zero, 0, 0, 0, timeout, description);
int lastError = Marshal.GetLastWin32Error();
NativeMethods.IsValidHandle(_hTrans, lastError);
}
}
/// Requests that the specified transaction be committed.
///
///
///
[SecurityCritical]
public void Commit()
{
if (!NativeMethods.IsAtLeastWindowsVista)
throw new PlatformNotSupportedException(Resources.Requires_Windows_Vista_Or_Higher);
if (!NativeMethods.CommitTransaction(_hTrans))
CheckTransaction();
}
/// Requests that the specified transaction be rolled back. This function is synchronous.
///
///
[SecurityCritical]
public void Rollback()
{
if (!NativeMethods.IsAtLeastWindowsVista)
throw new PlatformNotSupportedException(Resources.Requires_Windows_Vista_Or_Higher);
if (!NativeMethods.RollbackTransaction(_hTrans))
CheckTransaction();
}
private static void CheckTransaction()
{
uint error = (uint) Marshal.GetLastWin32Error();
int hr = Marshal.GetHRForLastWin32Error();
switch (error)
{
case Win32Errors.ERROR_TRANSACTION_ALREADY_ABORTED:
throw new TransactionAlreadyAbortedException("Transaction was already aborted", Marshal.GetExceptionForHR(hr));
case Win32Errors.ERROR_TRANSACTION_ALREADY_COMMITTED:
throw new TransactionAlreadyAbortedException("Transaction was already committed", Marshal.GetExceptionForHR(hr));
default:
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
break;
}
}
/// Gets the safe handle.
/// The safe handle.
public SafeHandle SafeHandle
{
get { return _hTrans; }
}
private readonly SafeKernelTransactionHandle _hTrans;
#region IDisposable Members
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
[SecurityPermissionAttribute(SecurityAction.Demand, UnmanagedCode = true)]
public void Dispose()
{
_hTrans.Close();
}
#endregion // IDisposable Members
}
}