You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

159 lines
7.1 KiB

/* 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);
}
/// <summary>A KTM transaction object for use with the transacted operations in <see cref="Filesystem"/></summary>
public sealed class KernelTransaction : MarshalByRefObject, IDisposable
{
/// <summary>Initializes a new instance of the <see cref="KernelTransaction"/> class, internally using the specified <see cref="Transaction"/>.
/// This method allows the usage of methods accepting a <see cref="KernelTransaction"/> with an instance of <see cref="System.Transactions.Transaction"/>.
/// </summary>
/// <param name="transaction">The transaction to use for any transactional operations.</param>
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
[SecurityCritical]
public KernelTransaction(Transaction transaction)
{
((IKernelTransaction) TransactionInterop.GetDtcTransaction(transaction)).GetHandle(out _hTrans);
}
/// <summary>Initializes a new instance of the <see cref="KernelTransaction"/> class with a default security descriptor, infinite timeout and no description.</summary>
[SecurityCritical]
public KernelTransaction()
: this(0, null)
{
}
/// <summary>Initializes a new instance of the <see cref="KernelTransaction"/> class with a default security descriptor.</summary>
/// <param name="timeout"><para>The time, in milliseconds, when the transaction will be aborted if it has not already reached the prepared state.</para></param>
/// <param name="description">A user-readable description of the transaction. This parameter may be <see langword="null"/>.</param>
[SecurityCritical]
public KernelTransaction(int timeout, string description)
: this(null, timeout, description)
{
}
/// <summary>Initializes a new instance of the <see cref="KernelTransaction"/> class.</summary>
/// <param name="securityDescriptor">The <see cref="ObjectSecurity"/> security descriptor.</param>
/// <param name="timeout"><para>The time, in milliseconds, when the transaction will be aborted if it has not already reached the prepared state.</para>
/// <para>Specify 0 to provide an infinite timeout.</para></param>
/// <param name="description">A user-readable description of the transaction. This parameter may be <see langword="null"/>.</param>
[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);
}
}
/// <summary>Requests that the specified transaction be committed.</summary>
/// <exception cref="TransactionAlreadyCommittedException"/>
/// <exception cref="TransactionAlreadyAbortedException"/>
/// <exception cref="Win32Exception"/>
[SecurityCritical]
public void Commit()
{
if (!NativeMethods.IsAtLeastWindowsVista)
throw new PlatformNotSupportedException(Resources.Requires_Windows_Vista_Or_Higher);
if (!NativeMethods.CommitTransaction(_hTrans))
CheckTransaction();
}
/// <summary>Requests that the specified transaction be rolled back. This function is synchronous.</summary>
/// <exception cref="TransactionAlreadyCommittedException"/>
/// <exception cref="Win32Exception"/>
[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;
}
}
/// <summary>Gets the safe handle.</summary>
/// <value>The safe handle.</value>
public SafeHandle SafeHandle
{
get { return _hTrans; }
}
private readonly SafeKernelTransactionHandle _hTrans;
#region IDisposable Members
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
[SecurityPermissionAttribute(SecurityAction.Demand, UnmanagedCode = true)]
public void Dispose()
{
_hTrans.Close();
}
#endregion // IDisposable Members
}
}