673 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			673 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Runtime.InteropServices;
 | |
| using Microsoft.Win32.SafeHandles;
 | |
| 
 | |
| namespace MaksIT.LTO.Core;
 | |
| 
 | |
| public partial class TapeDeviceHandler : IDisposable {
 | |
| 
 | |
|   #region Tape IOCTL Commands from ntddtape.h
 | |
| 
 | |
|   //
 | |
|   // NtDeviceIoControlFile IoControlCode values for this device.
 | |
|   //
 | |
|   // Warning:  Remember that the low two bits of the code specify how the
 | |
|   //           buffers are passed to the driver!
 | |
|   //
 | |
| 
 | |
|   private const uint IOCTL_TAPE_ERASE = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0000 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_PREPARE = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS) << 14) | (0x0001 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_WRITE_MARKS = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0002 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_GET_POSITION = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0003 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_SET_POSITION = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0004 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_GET_DRIVE_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0005 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_SET_DRIVE_PARAMS = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0006 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_GET_MEDIA_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0007 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_SET_MEDIA_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0008 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_GET_STATUS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0009 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_CREATE_PARTITION = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x000A << 2) | METHOD_BUFFERED;
 | |
| 
 | |
|   //
 | |
|   // The following device control codes are common for all class drivers.  The
 | |
|   // functions codes defined here must match all of the other class drivers.
 | |
|   //
 | |
|   // Warning: these codes will be replaced in the future with the IOCTL_STORAGE
 | |
|   // codes included below
 | |
|   //
 | |
| 
 | |
|   private const uint IOCTL_TAPE_MEDIA_REMOVAL = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0201 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_EJECT_MEDIA = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0202 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_LOAD_MEDIA = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0203 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_RESERVE = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0204 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_RELEASE = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0205 << 2) | METHOD_BUFFERED;
 | |
| 
 | |
|   private const uint IOCTL_TAPE_CHECK_VERIFY = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0200 << 2) | METHOD_BUFFERED;
 | |
|   private const uint IOCTL_TAPE_FIND_NEW_DEVICES = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0206 << 2) | METHOD_BUFFERED;
 | |
| 
 | |
|   #endregion
 | |
| 
 | |
|   #region Tape IOCTL Structures from ntddtape.h
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_ERASE definitions
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_ERASE_SHORT = 0;
 | |
|   public const uint TAPE_ERASE_LONG = 1;
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_ERASE {
 | |
|     public uint Type; // ULONG in C is equivalent to uint in C#
 | |
|     public byte Immediate; // BOOLEAN in C is typically a byte in C#
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_PREPARE definitions
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_LOAD = 0;
 | |
|   public const uint TAPE_UNLOAD = 1;
 | |
|   public const uint TAPE_TENSION = 2;
 | |
|   public const uint TAPE_LOCK = 3;
 | |
|   public const uint TAPE_UNLOCK = 4;
 | |
|   public const uint TAPE_FORMAT = 5;
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_PREPARE {
 | |
|     public uint Operation; // ULONG in C is equivalent to uint in C#
 | |
|     public byte Immediate; // BOOLEAN in C is typically a byte in C#
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_WRITE_MARKS definitions
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_SETMARKS = 0;
 | |
|   public const uint TAPE_FILEMARKS = 1;
 | |
|   public const uint TAPE_SHORT_FILEMARKS = 2;
 | |
|   public const uint TAPE_LONG_FILEMARKS = 3;
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_WRITE_MARKS {
 | |
|     public uint Type; // ULONG in C is equivalent to uint in C#
 | |
|     public uint Count; // ULONG in C is equivalent to uint in C#
 | |
|     public byte Immediate; // BOOLEAN in C is typically a byte in C#
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_SET_POSITION definitions
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_ABSOLUTE_POSITION = 0;
 | |
|   public const uint TAPE_LOGICAL_POSITION = 1;
 | |
|   public const uint TAPE_PSEUDO_LOGICAL_POSITION = 2;
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_GET_POSITION {
 | |
|     public uint Type;
 | |
|     public uint Partition;
 | |
|     public uint OffsetLow;
 | |
|     public uint OffsetHigh;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_SET_POSITION definitions
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_REWIND = 0;
 | |
|   public const uint TAPE_ABSOLUTE_BLOCK = 1;
 | |
|   public const uint TAPE_LOGICAL_BLOCK = 2;
 | |
|   public const uint TAPE_PSEUDO_LOGICAL_BLOCK = 3;
 | |
|   public const uint TAPE_SPACE_END_OF_DATA = 4;
 | |
|   public const uint TAPE_SPACE_RELATIVE_BLOCKS = 5;
 | |
|   public const uint TAPE_SPACE_FILEMARKS = 6;
 | |
|   public const uint TAPE_SPACE_SEQUENTIAL_FMKS = 7;
 | |
|   public const uint TAPE_SPACE_SETMARKS = 8;
 | |
|   public const uint TAPE_SPACE_SEQUENTIAL_SMKS = 9;
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_SET_POSITION {
 | |
|     public uint Method;
 | |
|     public uint Partition;
 | |
|     public long Offset;
 | |
|     public byte Immediate;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_GET_DRIVE_PARAMS definitions
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // Definitions for FeaturesLow parameter
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_DRIVE_FIXED = 0x00000001;
 | |
|   public const uint TAPE_DRIVE_SELECT = 0x00000002;
 | |
|   public const uint TAPE_DRIVE_INITIATOR = 0x00000004;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_ERASE_SHORT = 0x00000010;
 | |
|   public const uint TAPE_DRIVE_ERASE_LONG = 0x00000020;
 | |
|   public const uint TAPE_DRIVE_ERASE_BOP_ONLY = 0x00000040;
 | |
|   public const uint TAPE_DRIVE_ERASE_IMMEDIATE = 0x00000080;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_TAPE_CAPACITY = 0x00000100;
 | |
|   public const uint TAPE_DRIVE_TAPE_REMAINING = 0x00000200;
 | |
|   public const uint TAPE_DRIVE_FIXED_BLOCK = 0x00000400;
 | |
|   public const uint TAPE_DRIVE_VARIABLE_BLOCK = 0x00000800;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_WRITE_PROTECT = 0x00001000;
 | |
|   public const uint TAPE_DRIVE_EOT_WZ_SIZE = 0x00002000;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_ECC = 0x00010000;
 | |
|   public const uint TAPE_DRIVE_COMPRESSION = 0x00020000;
 | |
|   public const uint TAPE_DRIVE_PADDING = 0x00040000;
 | |
|   public const uint TAPE_DRIVE_REPORT_SMKS = 0x00080000;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_GET_ABSOLUTE_BLK = 0x00100000;
 | |
|   public const uint TAPE_DRIVE_GET_LOGICAL_BLK = 0x00200000;
 | |
|   public const uint TAPE_DRIVE_SET_EOT_WZ_SIZE = 0x00400000;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_EJECT_MEDIA = 0x01000000; //don't use this bit!
 | |
|                                                          //                                                     //can't be a low features bit!
 | |
|                                                          //                                                     //reserved; high features only
 | |
| 
 | |
|   //
 | |
|   // Definitions for FeaturesHigh parameter
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_DRIVE_LOAD_UNLOAD = 0x80000001;
 | |
|   public const uint TAPE_DRIVE_TENSION = 0x80000002;
 | |
|   public const uint TAPE_DRIVE_LOCK_UNLOCK = 0x80000004;
 | |
|   public const uint TAPE_DRIVE_REWIND_IMMEDIATE = 0x80000008;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_SET_BLOCK_SIZE = 0x80000010;
 | |
|   public const uint TAPE_DRIVE_LOAD_UNLD_IMMED = 0x80000040;
 | |
|   public const uint TAPE_DRIVE_TENSION_IMMED = 0x80000080;
 | |
|   public const uint TAPE_DRIVE_LOCK_UNLK_IMMED = 0x80000100;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_SET_ECC = 0x80000200;
 | |
|   public const uint TAPE_DRIVE_SET_COMPRESSION = 0x80000400;
 | |
|   public const uint TAPE_DRIVE_SET_PADDING = 0x80000800;
 | |
|   public const uint TAPE_DRIVE_SET_REPORT_SMKS = 0x80001000;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_ABSOLUTE_BLK = 0x80002000;
 | |
|   public const uint TAPE_DRIVE_ABS_BLK_IMMED = 0x80004000;
 | |
|   public const uint TAPE_DRIVE_LOGICAL_BLK = 0x80008000;
 | |
|   public const uint TAPE_DRIVE_LOG_BLK_IMMED = 0x80010000;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_END_OF_DATA = 0x80020000;
 | |
|   public const uint TAPE_DRIVE_RELATIVE_BLKS = 0x80040000;
 | |
|   public const uint TAPE_DRIVE_FILEMARKS = 0x80080000;
 | |
|   public const uint TAPE_DRIVE_SEQUENTIAL_FMKS = 0x80100000;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_SETMARKS = 0x80200000;
 | |
|   public const uint TAPE_DRIVE_SEQUENTIAL_SMKS = 0x80400000;
 | |
|   public const uint TAPE_DRIVE_REVERSE_POSITION = 0x80800000;
 | |
|   public const uint TAPE_DRIVE_SPACE_IMMEDIATE = 0x81000000;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_WRITE_SETMARKS = 0x82000000;
 | |
|   public const uint TAPE_DRIVE_WRITE_FILEMARKS = 0x84000000;
 | |
|   public const uint TAPE_DRIVE_WRITE_SHORT_FMKS = 0x88000000;
 | |
|   public const uint TAPE_DRIVE_WRITE_LONG_FMKS = 0x90000000;
 | |
| 
 | |
|   public const uint TAPE_DRIVE_WRITE_MARK_IMMED = 0xA0000000;
 | |
|   public const uint TAPE_DRIVE_FORMAT = 0xC0000000;
 | |
|   public const uint TAPE_DRIVE_FORMAT_IMMEDIATE = 0x80000000;
 | |
|   public const uint TAPE_DRIVE_HIGH_FEATURES = 0x80000000; //mask for high features flag
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_GET_DRIVE_PARAMETERS {
 | |
|     public uint ECC;
 | |
|     public uint Compression;
 | |
|     public uint DataPadding;
 | |
|     public uint ReportSetmarks;
 | |
|     public uint DefaultBlockSize;
 | |
|     public uint MaximumBlockSize;
 | |
|     public uint MinimumBlockSize;
 | |
|     public uint MaximumPartitionCount;
 | |
|     public uint FeaturesLow;
 | |
|     public uint FeaturesHigh;
 | |
|     public uint EOTWarningZoneSize;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_SET_DRIVE_PARAMETERS definitions
 | |
|   //
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_SET_DRIVE_PARAMETERS {
 | |
|     public uint ECC;
 | |
|     public uint Compression;
 | |
|     public uint DataPadding;
 | |
|     public uint ReportSetmarks;
 | |
|     public uint EOTWarningZoneSize;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_GET_MEDIA_PARAMETERS definitions
 | |
|   //
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_GET_MEDIA_PARAMETERS {
 | |
|     public uint Capacity;
 | |
|     public uint Remaining;
 | |
|     public uint BlockSize;
 | |
|     public uint PartitionCount;
 | |
|     public byte WriteProtected;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_SET_MEDIA_PARAMETERS definitions
 | |
|   //
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_SET_MEDIA_PARAMETERS {
 | |
|     public uint BlockSize;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // IOCTL_TAPE_CREATE_PARTITION definitions
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_FIXED_PARTITIONS = 0;
 | |
|   public const uint TAPE_SELECT_PARTITIONS = 1;
 | |
|   public const uint TAPE_INITIATOR_PARTITIONS = 2;
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_CREATE_PARTITION {
 | |
|     public uint Method;
 | |
|     public uint Count;
 | |
|     public uint Size;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // WMI Methods
 | |
|   //
 | |
| 
 | |
|   public const uint TAPE_QUERY_DRIVE_PARAMETERS = 0;
 | |
|   public const uint TAPE_QUERY_MEDIA_CAPACITY = 1;
 | |
|   public const uint TAPE_CHECK_FOR_DRIVE_PROBLEM = 2;
 | |
|   public const uint TAPE_QUERY_IO_ERROR_DATA = 3;
 | |
|   public const uint TAPE_QUERY_DEVICE_ERROR_DATA = 4;
 | |
| 
 | |
|   [StructLayout(LayoutKind.Sequential)]
 | |
|   public struct TAPE_WMI_OPERATIONS {
 | |
|     public uint Method;
 | |
|     public uint DataBufferSize;
 | |
|     public IntPtr DataBuffer;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Type of drive errors
 | |
|   //
 | |
| 
 | |
|   public enum TAPE_DRIVE_PROBLEM_TYPE {
 | |
|     TapeDriveProblemNone,
 | |
|     TapeDriveReadWriteWarning,
 | |
|     TapeDriveReadWriteError,
 | |
|     TapeDriveReadWarning,
 | |
|     TapeDriveWriteWarning,
 | |
|     TapeDriveReadError,
 | |
|     TapeDriveWriteError,
 | |
|     TapeDriveHardwareError,
 | |
|     TapeDriveUnsupportedMedia,
 | |
|     TapeDriveScsiConnectionError,
 | |
|     TapeDriveTimetoClean,
 | |
|     TapeDriveCleanDriveNow,
 | |
|     TapeDriveMediaLifeExpired,
 | |
|     TapeDriveSnappedTape
 | |
|   }
 | |
|   #endregion
 | |
| 
 | |
|   #region Tape IOCTL Methods from ntddtape.h
 | |
| 
 | |
|   /// <summary>
 | |
|   /// Erase the tape
 | |
|   /// </summary>
 | |
|   /// <param name="type">The type of erase operation. Valid values are <see cref="TAPE_ERASE_SHORT"/> and <see cref="TAPE_ERASE_LONG"/>.</param>
 | |
|   public void Erase(uint type) {
 | |
|     TAPE_ERASE erase = new TAPE_ERASE {
 | |
|       Type = type,
 | |
|       Immediate = 0
 | |
|     };
 | |
| 
 | |
|     IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(erase));
 | |
| 
 | |
|     try {
 | |
|       Marshal.StructureToPtr(erase, inBuffer, false);
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_ERASE, inBuffer, (uint)Marshal.SizeOf(erase), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (result) {
 | |
|         Console.WriteLine($"Erase Tape ({type}): Success");
 | |
|       }
 | |
|       else {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Erase Tape: Failed with error code {error}");
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(inBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// <summary>
 | |
|   /// Prepare the tape for a specific operation
 | |
|   /// </summary>
 | |
|   /// <param name="operation">The type of prepare operation. Valid values are <see cref="TAPE_LOAD"/>, <see cref="TAPE_UNLOAD"/>, <see cref="TAPE_UNLOCK"/> and <see cref="TAPE_FORMAT"/>.</param>
 | |
|   public void Prepare(uint operation) {
 | |
|     TAPE_PREPARE prepare = new TAPE_PREPARE {
 | |
|       Operation = operation,
 | |
|       Immediate = 0
 | |
|     };
 | |
| 
 | |
|     IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(prepare));
 | |
| 
 | |
|     try {
 | |
|       Marshal.StructureToPtr(prepare, inBuffer, false);
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_PREPARE, inBuffer, (uint)Marshal.SizeOf(prepare), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (result) {
 | |
|         Console.WriteLine($"Prepare Tape ({operation}): Success");
 | |
|       }
 | |
|       else {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Prepare Tape: Failed with error code {error}");
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(inBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// <summary>
 | |
|   /// Write tape marks
 | |
|   /// <param name="type">The type of marks to write. Valid values are <see cref="TAPE_SETMARKS"/>, <see cref="TAPE_FILEMARKS"/>, <see cref="TAPE_SHORT_FILEMARKS"/> and <see cref="TAPE_LONG_FILEMARKS"/>.</param>
 | |
|   /// <param name="count">The number of marks to write.</param>
 | |
|   public void WriteMarks(uint type, uint count) {
 | |
|     TAPE_WRITE_MARKS marks = new TAPE_WRITE_MARKS {
 | |
|       Type = type,
 | |
|       Count = count,
 | |
|       Immediate = 0
 | |
|     };
 | |
| 
 | |
|     IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(marks));
 | |
| 
 | |
|     try {
 | |
|       Marshal.StructureToPtr(marks, inBuffer, false);
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_WRITE_MARKS, inBuffer, (uint)Marshal.SizeOf(marks), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (result) {
 | |
|         Console.WriteLine("Write Marks: Success");
 | |
|       }
 | |
|       else {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Write Marks: Failed with error code {error}");
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(inBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   public class TapePosition {
 | |
|     public uint? MethodType { get; set; }
 | |
|     public uint? Partition { get; set; }
 | |
|     public uint? OffsetLow { get; set; }
 | |
|     public uint? OffsetHigh { get; set; }
 | |
|     public int? Error { get; set; }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /// <summary>
 | |
|   /// Get the current tape position
 | |
|   /// </summary>
 | |
|   /// <param name="type">The type of position to get. Valid values are <see cref="TAPE_ABSOLUTE_POSITION"/>, <see cref="TAPE_LOGICAL_POSITION"/> and <see cref="TAPE_PSEUDO_LOGICAL_POSITION"/>.</param>
 | |
|   /// <param name="partition">The partition number.</param>
 | |
|   /// <param name="offsetLow">The low offset value.</param>
 | |
|   /// <param name="offsetHigh">The high offset value.</param>
 | |
|   /// <returns>The tape position <see cref="TapePosition"/>.</returns>
 | |
|   public TapePosition GetPosition(uint type, uint partition = 0, uint offsetLow = 0, uint offsetHigh = 0) {
 | |
|     TAPE_GET_POSITION position = new TAPE_GET_POSITION {
 | |
|       Type = 0,
 | |
|       Partition = partition,
 | |
|       OffsetLow = offsetLow,
 | |
|       OffsetHigh = offsetHigh
 | |
|     };
 | |
| 
 | |
|     IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(position));
 | |
| 
 | |
|     try {
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_POSITION, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(position), out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (result) {
 | |
|         position = Marshal.PtrToStructure<TAPE_GET_POSITION>(outBuffer);
 | |
|         Console.WriteLine("Get Position: Success");
 | |
|         Console.WriteLine($"Type: {position.Type}");
 | |
|         Console.WriteLine($"Partition: {position.Partition}");
 | |
|         Console.WriteLine($"OffsetLow: {position.OffsetLow}");
 | |
|         Console.WriteLine($"OffsetHigh: {position.OffsetHigh}");
 | |
| 
 | |
|         return new TapePosition {
 | |
|           MethodType = position.Type,
 | |
|           Partition = position.Partition,
 | |
|           OffsetLow = position.OffsetLow,
 | |
|           OffsetHigh = position.OffsetHigh
 | |
|         };
 | |
|       }
 | |
|       else {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Get Position: Failed with error code {error}");
 | |
| 
 | |
|         return new TapePosition {
 | |
|           Error = error
 | |
|         };
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(outBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// <summary>
 | |
|   /// Set the tape position
 | |
|   /// </summary>
 | |
|   /// <param name="method">The method to use for setting the position. Valid values are <see cref="TAPE_REWIND"/>, <see cref="TAPE_ABSOLUTE_BLOCK"/>, <see cref="TAPE_LOGICAL_BLOCK"/>, <see cref="TAPE_PSEUDO_LOGICAL_BLOCK"/>, <see cref="TAPE_SPACE_END_OF_DATA"/>, <see cref="TAPE_SPACE_RELATIVE_BLOCKS"/>, <see cref="TAPE_SPACE_FILEMARKS"/>, <see cref="TAPE_SPACE_SEQUENTIAL_FMKS"/>, <see cref="TAPE_SPACE_SETMARKS"/> and <see cref="TAPE_SPACE_SEQUENTIAL_SMKS"/>.</param>
 | |
|   /// <param name="partition">The partition number.</param>
 | |
|   /// <param name="offset">The offset value.</param> 
 | |
|   public void SetPosition(uint method, uint partition = 0, long offset = 0) {
 | |
|     TAPE_SET_POSITION position = new TAPE_SET_POSITION {
 | |
|       Method = method,
 | |
|       Partition = partition,
 | |
|       Offset = offset,
 | |
|       Immediate = 0
 | |
|     };
 | |
| 
 | |
|     IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(position));
 | |
|     try {
 | |
|       Marshal.StructureToPtr(position, inBuffer, false);
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_POSITION, inBuffer, (uint)Marshal.SizeOf(position), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (result) {
 | |
|         Console.WriteLine($"Set Position ({method}): Success");
 | |
|       }
 | |
|       else {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Set Position ({method}): Failed with error code {error}");
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(inBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// <summary>
 | |
|   /// Get the drive parameters
 | |
|   /// </summary>
 | |
|   /// <param name="maxBlockSize">The maximum block size supported by the drive.</param>
 | |
|   /// <param name="minBlockSize">The minimum block size supported by the drive.</param> 
 | |
|   public void GetDriveParameters(out uint minBlockSize, out uint maxBlockSize) {
 | |
|     TAPE_GET_DRIVE_PARAMETERS driveParams = new TAPE_GET_DRIVE_PARAMETERS {
 | |
|       ECC = 0,
 | |
|       Compression = 0,
 | |
|       DataPadding = 0,
 | |
|       ReportSetmarks = 0,
 | |
|       DefaultBlockSize = 0,
 | |
|       MaximumBlockSize = 0,
 | |
|       MinimumBlockSize = 0,
 | |
|       MaximumPartitionCount = 0,
 | |
|       FeaturesLow = 0,
 | |
|       FeaturesHigh = 0,
 | |
|       EOTWarningZoneSize = 0
 | |
|     };
 | |
| 
 | |
|     IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(driveParams));
 | |
|     try {
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_DRIVE_PARAMS, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(driveParams), out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (result) {
 | |
|         driveParams = Marshal.PtrToStructure<TAPE_GET_DRIVE_PARAMETERS>(outBuffer);
 | |
|         minBlockSize = driveParams.MinimumBlockSize;
 | |
|         maxBlockSize = driveParams.MaximumBlockSize;
 | |
|         Console.WriteLine($"Drive Parameters: MinBlockSize = {minBlockSize}, MaxBlockSize = {maxBlockSize}");
 | |
|       }
 | |
|       else {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Get Drive Parameters Failed with error code {error}");
 | |
|         minBlockSize = 0;
 | |
|         maxBlockSize = 0;
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(outBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// <summary>
 | |
|   /// Set the drive parameters
 | |
|   /// </summary>
 | |
|   /// <param name="ecc">The error correction code (ECC) setting.</param>
 | |
|   /// <param name="compression">The compression setting.</param>
 | |
|   /// <param name="dataPadding">The data padding setting.</param>
 | |
|   /// <param name="reportSetmarks">The report setmarks setting.</param>
 | |
|   /// <param name="eotWarningZoneSize">The end-of-tape (EOT) warning zone size setting.</param> 
 | |
|   public void SetDriveParams(uint ecc, uint compression = 0, uint dataPadding = 0, uint reportSetmarks = 0, uint eotWarningZoneSize = 0) {
 | |
|     TAPE_SET_DRIVE_PARAMETERS driveParams = new TAPE_SET_DRIVE_PARAMETERS {
 | |
|       ECC = ecc,
 | |
|       Compression = compression,
 | |
|       DataPadding = dataPadding,
 | |
|       ReportSetmarks = reportSetmarks,
 | |
|       EOTWarningZoneSize = eotWarningZoneSize
 | |
|     };
 | |
| 
 | |
|     IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(driveParams));
 | |
| 
 | |
|     try {
 | |
|       Marshal.StructureToPtr(driveParams, inBuffer, false);
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_DRIVE_PARAMS, inBuffer, (uint)Marshal.SizeOf(driveParams), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (result) {
 | |
|         Console.WriteLine("Set Drive Params: Success");
 | |
|       }
 | |
|       else {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Set Drive Params Failed with error code {error}");
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(inBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   public void GetMediaParams(uint capacity = 0, uint remaining = 0, uint blockSize = 0, uint partitionCount = 0, byte writeProtected = 0) {
 | |
|     TAPE_GET_MEDIA_PARAMETERS mediaParams = new TAPE_GET_MEDIA_PARAMETERS {
 | |
|       Capacity = 0,
 | |
|       Remaining = 0,
 | |
|       BlockSize = 0,
 | |
|       PartitionCount = 0,
 | |
|       WriteProtected = 0
 | |
|     };
 | |
| 
 | |
|     IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(mediaParams));
 | |
| 
 | |
|     try {
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_MEDIA_PARAMS, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(mediaParams), out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (result) {
 | |
|         mediaParams = Marshal.PtrToStructure<TAPE_GET_MEDIA_PARAMETERS>(outBuffer);
 | |
|         Console.WriteLine("Get Media Params: Success");
 | |
|         Console.WriteLine($"Capacity: {mediaParams.Capacity}");
 | |
|         Console.WriteLine($"Remaining: {mediaParams.Remaining}");
 | |
|         Console.WriteLine($"BlockSize: {mediaParams.BlockSize}");
 | |
|         Console.WriteLine($"PartitionCount: {mediaParams.PartitionCount}");
 | |
|         Console.WriteLine($"WriteProtected: {mediaParams.WriteProtected}");
 | |
|       }
 | |
|       else {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Get Media Params Failed with error code {error}");
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(outBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   public void SetMediaParams(uint blockSize) {
 | |
|     TAPE_SET_MEDIA_PARAMETERS mediaParams = new TAPE_SET_MEDIA_PARAMETERS {
 | |
|       BlockSize = blockSize
 | |
|     };
 | |
| 
 | |
|     IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(mediaParams));
 | |
| 
 | |
|     try {
 | |
|       Marshal.StructureToPtr(mediaParams, inBuffer, false);
 | |
|       bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_MEDIA_PARAMS, inBuffer, (uint)Marshal.SizeOf(mediaParams), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|       if (!result) {
 | |
|         int error = Marshal.GetLastWin32Error();
 | |
|         Console.WriteLine($"Set Block Size Failed with error code {error}");
 | |
|       }
 | |
|       else {
 | |
|         Console.WriteLine($"Set Block Size ({blockSize}): Success");
 | |
|       }
 | |
|     }
 | |
|     finally {
 | |
|       Marshal.FreeHGlobal(inBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   public int GetStatus() {
 | |
|     bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_STATUS, IntPtr.Zero, 0, IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|     if (result) {
 | |
|       Console.WriteLine("Get Status: Success");
 | |
|     }
 | |
|     else {
 | |
|       int error = Marshal.GetLastWin32Error();
 | |
|       Console.WriteLine($"Get Status: Failed with error code {error}");
 | |
| 
 | |
|       return error;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   public void CreatePartition(uint method = 0, uint count = 0, uint size = 0) {
 | |
|     TAPE_CREATE_PARTITION partition = new TAPE_CREATE_PARTITION {
 | |
|       Method = 0,
 | |
|       Count = 1,
 | |
|       Size = 0
 | |
|     };
 | |
| 
 | |
|     bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_CREATE_PARTITION, IntPtr.Zero, 0, IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
 | |
| 
 | |
|     if (!result) {
 | |
|       int error = Marshal.GetLastWin32Error();
 | |
|       Console.WriteLine($"Create Partition: Failed with error code {error}");
 | |
|     }
 | |
|     else {
 | |
|       Console.WriteLine("Create Partition: Success");
 | |
|     }
 | |
|   }
 | |
|   #endregion
 | |
| } |