/* 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 } }