Creating multiple partitions on USB using C#

Iam trying to use DeviceIOControl to create multiple partiions in USB. It is always creating only one partition.

Here is my source code

[DllImport(“kernel32.dll”, SetLastError = true)]
static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);

[DllImport(“kernel32”)]
static extern int CloseHandle(IntPtr handle);

[DllImport(“kernel32”)]
private static extern int DeviceIoControl
(IntPtr deviceHandle, uint ioControlCode,
IntPtr inBuffer, int inBufferSize,
IntPtr outBuffer, int outBufferSize,
ref int bytesReturned, IntPtr overlapped);

public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
public const uint FILE_SHARE_READ = 0x00000001;
public const uint FILE_SHARE_WRITE = 0x00000002;
public const uint OPEN_EXISTING = 0x00000003;
public const uint FILE_ATTRIBUTE_NORMAL = 0x80;
public const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x90083;
public const int DRIVE_ACCESS_RETRIES = 10;
public const int DRIVE_ACCESS_TIMEOUT = 15000;
public const uint FSCTL_LOCK_VOLUME = 0x00090018;
static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
public const uint IOCTL_DISK_GET_DRIVE_LAYOUT_EX = 0x00070050;
public const uint IOCTL_DISK_GET_DRIVE_GEOMETRY_EX = 0x000700A0;
public const uint IOCTL_DISK_CREATE_DISK = 0x0007C058;
public const uint IOCTL_DISK_UPDATE_PROPERTIES = 0x00070140;
public const uint IOCTL_DISK_SET_DRIVE_LAYOUT_EX = 0x0007C054;
public const int MIN_EXTRA_PART_SIZE = 1024 * 1024;
public static int mediaType = 0;

///
/// Describes the geometry of disk devices and media.
///
[StructLayout(LayoutKind.Explicit)]
public struct DISK_GEOMETRY
{
///
/// The number of cylinders.
///
[FieldOffset(0)]
public Int64 Cylinders;

///
/// The type of media. For a list of values, see MEDIA_TYPE.
///
[FieldOffset(8)]
public MEDIA_TYPE MediaType;

///
/// The number of tracks per cylinder.
///
[FieldOffset(12)]
public uint TracksPerCylinder;

///
/// The number of sectors per track.
///
[FieldOffset(16)]
public uint SectorsPerTrack;

///
/// The number of bytes per sector.
///
[FieldOffset(20)]
public uint BytesPerSector;
}

///
/// Describes the extended geometry of disk devices and media.
///
[StructLayout(LayoutKind.Explicit)]
private struct DISK_GEOMETRY_EX
{
///
/// A DISK_GEOMETRY structure.
///
[FieldOffset(0)]
public DISK_GEOMETRY Geometry;

///
/// The disk size, in bytes.
///
[FieldOffset(24)]
public Int64 DiskSize;

///
/// Any additional data.
///
[FieldOffset(32)]
public Byte Data;
}

///
/// Represents the format of a partition.
///
public enum PARTITION_STYLE : uint
{
///
/// Master boot record (MBR) format.
///
PARTITION_STYLE_MBR = 0,

///
/// GUID Partition Table (GPT) format.
///
PARTITION_STYLE_GPT = 1,

///
/// Partition not formatted in either of the recognized formats?MBR or GPT.
///
PARTITION_STYLE_RAW = 2
}

///
/// Contains partition information specific to master boot record (MBR) disks.
///
[StructLayout(LayoutKind.Explicit)]
public struct PARTITION_INFORMATION_MBR
{
#region Constants
///
/// An unused entry partition.
///
public const byte PARTITION_ENTRY_UNUSED = 0x00;

///
/// A FAT12 file system partition.
///
public const byte PARTITION_FAT_12 = 0x01;

///
/// A FAT16 file system partition.
///
public const byte PARTITION_FAT_16 = 0x04;

///
/// An extended partition.
///
public const byte PARTITION_EXTENDED = 0x05;

///
/// An IFS partition.
///
public const byte PARTITION_IFS = 0x07;

///
/// A FAT32 file system partition.
///
public const byte PARTITION_FAT32 = 0x0B;

///
/// A logical disk manager (LDM) partition.
///
public const byte PARTITION_LDM = 0x42;

///
/// An NTFT partition.
///
public const byte PARTITION_NTFT = 0x80;

///
/// A valid NTFT partition.
///
/// The high bit of a partition type code indicates that a partition is part of an NTFT mirror or striped array.
///
public const byte PARTITION_VALID_NTFT = 0xC0;
#endregion

///
/// The type of partition. For a list of values, see Disk Partition Types.
///
[FieldOffset(0)]
[MarshalAs(UnmanagedType.U1)]
public byte PartitionType;

///
/// If this member is TRUE, the partition is bootable.
///
[FieldOffset(1)]
[MarshalAs(UnmanagedType.I1)]
public bool BootIndicator;

///
/// If this member is TRUE, the partition is of a recognized type.
///
[FieldOffset(2)]
[MarshalAs(UnmanagedType.I1)]
public bool RecognizedPartition;

///
/// The number of hidden sectors in the partition.
///
[FieldOffset(4)]
public uint HiddenSectors;
}

///
/// Contains GUID partition table (GPT) partition information.
///
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct PARTITION_INFORMATION_GPT
{
///
/// A GUID that identifies the partition type.
///
/// Each partition type that the EFI specification supports is identified by its own GUID, which is
/// published by the developer of the partition.
///
[FieldOffset(0)]
public Guid PartitionType;

///
/// The GUID of the partition.
///
[FieldOffset(16)]
public Guid PartitionId;

///
/// The Extensible Firmware Interface (EFI) attributes of the partition.
///
///
[FieldOffset(32)]
public UInt64 Attributes;

///
/// A wide-character string that describes the partition.
///
[FieldOffset(40)]
public string Name;
}
///
/// Provides information about a drive’s master boot record (MBR) partitions.
///
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_MBR
{
///
/// The signature of the drive.
///
[FieldOffset(0)]
public uint Signature;
}

///
/// Contains information about a drive’s GUID partition table (GPT) partitions.
///
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_GPT
{
///
/// The GUID of the disk.
///
[FieldOffset(0)]
public Guid DiskId;

///
/// The starting byte offset of the first usable block.
///
[FieldOffset(16)]
public Int64 StartingUsableOffset;

///
/// The size of the usable blocks on the disk, in bytes.
///
[FieldOffset(24)]
public Int64 UsableLength;

///
/// The maximum number of partitions that can be defined in the usable block.
///
[FieldOffset(32)]
public uint MaxPartitionCount;
}

///
/// Contains information about a disk partition.
///
[StructLayout(LayoutKind.Explicit)]
public struct PARTITION_INFORMATION_EX
{
///
/// The format of the partition. For a list of values, see PARTITION_STYLE.
///
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;

///
/// The starting offset of the partition.
///
[FieldOffset(8)]
public Int64 StartingOffset;

///
/// The length of the partition, in bytes.
///
[FieldOffset(16)]
public Int64 PartitionLength;

///
/// The number of the partition (1-based).
///
[FieldOffset(24)]
public uint PartitionNumber;

///
/// If this member is TRUE, the partition information has changed. When you change a partition (with
/// IOCTL_DISK_SET_DRIVE_LAYOUT), the system uses this member to determine which partitions have changed
/// and need their information rewritten.
///
[FieldOffset(28)]
[MarshalAs(UnmanagedType.I1)]
public bool RewritePartition;

///
/// A PARTITION_INFORMATION_MBR structure that specifies partition information specific to master boot
/// record (MBR) disks. The MBR partition format is the standard AT-style format.
///
[FieldOffset(32)]
public PARTITION_INFORMATION_MBR Mbr;

///
/// A PARTITION_INFORMATION_GPT structure that specifies partition information specific to GUID partition
/// table (GPT) disks. The GPT format corresponds to the EFI partition format.
///
[FieldOffset(32)]
public PARTITION_INFORMATION_GPT Gpt;
}

[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_UNION
{
[FieldOffset(0)]
public DRIVE_LAYOUT_INFORMATION_MBR Mbr;

[FieldOffset(0)]
public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
}

///
/// Contains extended information about a drive’s partitions.
///
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_EX
{
///
/// The style of the partitions on the drive enumerated by the PARTITION_STYLE enumeration.
///
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;

///
/// The number of partitions on a drive.
///
/// On disks with the MBR layout, this value is always a multiple of 4. Any partitions that are unused have
/// a partition type of PARTITION_ENTRY_UNUSED.
///
[FieldOffset(4)]
public uint PartitionCount;

///
/// A DRIVE_LAYOUT_INFORMATION_MBR structure containing information about the master boot record type
/// partitioning on the drive.
///
[FieldOffset(8)]
public DRIVE_LAYOUT_INFORMATION_UNION Mbr;

///
/// A DRIVE_LAYOUT_INFORMATION_GPT structure containing information about the GUID disk partition type
/// partitioning on the drive.
///
// [FieldOffset(8)]
//public DRIVE_LAYOUT_INFORMATION_GPT Gpt;

///
/// A variable-sized array of PARTITION_INFORMATION_EX structures, one structure for each partition on the
/// drive.
///
[FieldOffset(48)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 4)]
public PARTITION_INFORMATION_EX PartitionEntry;

}

[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK_MBR
{
[FieldOffset(0)]
public uint Signature;
}

[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK_GPT
{
[FieldOffset(0)]
public Guid DiskId;

[FieldOffset(16)]
public uint MaxPartitionCount;
}

[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK
{
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;

[FieldOffset(4)]
public CREATE_DISK_MBR Mbr;

[FieldOffset(4)]
public CREATE_DISK_GPT Gpt;
}

static IntPtr GetHandle(int driveIndex)
{
IntPtr handle;
//bool locked = false;

Program p = new Program();
string physicalName = p.GetPhysicalName(driveIndex);

Debug.WriteLine(physicalName);

handle = CreateFile(physicalName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

if (handle == INVALID_HANDLE_VALUE)
{
Debug.WriteLine(Marshal.GetLastWin32Error());
return IntPtr.Zero;
}
return handle;
}

//Returns true if drive Index is successfully created
//Returns false if not created successfully
static bool CreatePartition(int driveIndex)
{
IntPtr handle = GetHandle(driveIndex);

if (handle == INVALID_HANDLE_VALUE)
{
return false;
}

//Step 2: IOCTL_DISK_GET_DRIVE_GEOMETRY_EX to get the physical disk’s geometry ( we need some information in it to fill partition data)
//The number of surfaces (or heads, which is the same thing), cylinders, and sectors vary a lot; the specification of the number of each is called the geometry of a hard disk.
//The geometry is usually stored in a special, battery-powered memory location called the CMOS RAM , from where the operating system can fetch it during bootup or driver initialization.
int size = 0;
DISK_GEOMETRY_EX geometry = new DISK_GEOMETRY_EX();
IntPtr lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DISK_GEOMETRY_EX)));
Marshal.StructureToPtr(geometry, lpOutBuffer, false);
int result = DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, IntPtr.Zero, 0, lpOutBuffer,
Marshal.SizeOf(typeof(DISK_GEOMETRY_EX)),
ref size, IntPtr.Zero);
geometry = (DISK_GEOMETRY_EX)Marshal.PtrToStructure(lpOutBuffer, typeof(DISK_GEOMETRY_EX));

//Step 3: IOCTL_DISK_CREATE_DISK is used to initialize a disk with an empty partition table.
CREATE_DISK createDisk = new CREATE_DISK();
createDisk.PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
createDisk.Mbr.Signature = 1;

IntPtr createDiskBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CREATE_DISK)));
Marshal.StructureToPtr(createDisk, createDiskBuffer, false);

byte arr1 = new byte[Marshal.SizeOf(typeof(CREATE_DISK))];
Marshal.Copy(createDiskBuffer, arr1, 0, Marshal.SizeOf(typeof(CREATE_DISK)));

result = DeviceIoControl(handle, IOCTL_DISK_CREATE_DISK, createDiskBuffer, Marshal.SizeOf(typeof(CREATE_DISK)),
IntPtr.Zero, 0, ref size, IntPtr.Zero);

result = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, ref size, IntPtr.Zero);

//Step 4: IOCTL_DISK_SET_DRIVE_LAYOUT_EX to repartition a disk as specified.
//Note: use IOCTL_DISK_UPDATE_PROPERTIES to synchronize system view after IOCTL_DISK_CREATE_DISK and IOCTL_DISK_SET_DRIVE_LAYOUT_EX
/* DWORD driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX) * 4 * 25;
DRIVE_LAYOUT_INFORMATION_EX *DriveLayoutEx = (DRIVE_LAYOUT_INFORMATION_EX *) new BYTE[driveLayoutSize];*/

IntPtr driveLayoutbuffer = Marshal.AllocHGlobal(624);
DRIVE_LAYOUT_INFORMATION_EX driveLayoutEx = new DRIVE_LAYOUT_INFORMATION_EX();
int pn = 0;
driveLayoutEx.PartitionEntry = new PARTITION_INFORMATION_EX[4];

mediaType = (int)geometry.Geometry.MediaType;
Int64 bytes_per_track = (geometry.Geometry.SectorsPerTrack) * (geometry.Geometry.BytesPerSector);

driveLayoutEx.PartitionEntry[pn].StartingOffset = 0x123;

Int64 main_part_size_in_sectors, extra_part_size_in_sectors = 0;
main_part_size_in_sectors = (geometry.DiskSize - driveLayoutEx.PartitionEntry[pn].StartingOffset) / geometry.Geometry.BytesPerSector;

if (main_part_size_in_sectors <= 0)
{
return false;
}

extra_part_size_in_sectors = (MIN_EXTRA_PART_SIZE + bytes_per_track - 1) / bytes_per_track;
main_part_size_in_sectors = ((main_part_size_in_sectors / geometry.Geometry.SectorsPerTrack) -
extra_part_size_in_sectors) * geometry.Geometry.SectorsPerTrack;
if (main_part_size_in_sectors <= 0)
{
return false;
}

driveLayoutEx.PartitionEntry[pn].PartitionLength = 50000;
driveLayoutEx.PartitionEntry[pn].PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x07;
driveLayoutEx.PartitionEntry[pn].Mbr.BootIndicator = true;

pn++;

// Set the optional extra partition
// Should end on a track boundary
driveLayoutEx.PartitionEntry[pn].StartingOffset = 0x400;
driveLayoutEx.PartitionEntry[pn].PartitionLength = 26244; //TODO: Has to change
driveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0xef;

pn++;

for (uint i = 0; i < pn; i++)
{
driveLayoutEx.PartitionEntry[i].PartitionNumber = i + 1;
driveLayoutEx.PartitionEntry[i].PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionEntry[i].RewritePartition = true;
}

driveLayoutEx.PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionCount = 4; //It should be a multiple of 4
driveLayoutEx.Mbr.Mbr.Signature = createDisk.Mbr.Signature;

Marshal.StructureToPtr(driveLayoutEx, driveLayoutbuffer, false);

result = DeviceIoControl(handle, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, driveLayoutbuffer, 624, IntPtr.Zero, 0, ref size, IntPtr.Zero);
result = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, ref size, IntPtr.Zero);

Marshal.FreeHGlobal(driveLayoutbuffer);
Marshal.FreeHGlobal(createDiskBuffer);

Marshal.FreeHGlobal(lpOutBuffer);

return true;
}

It only creates one partition with offset to zero and partition length to full size of USB.

Iam trying this from past two days but still no solution.

Please help

You’ll find this forum is geared toward Windows driver development. Your question falls squarely in the user space development realm. You will find better success asking this type of question some place like stackoverflow.com.

If it were me, I’d just use diskpart to accomplish this. A batch file or powershell script would suffice.

Oh my God, this is EXACTLY the place where managed code makes the solution much worse.

Managed code and C# should simplify the solution, not make it full of pInvoke scum, where it is too easy to make a bug and too hard to find it.

pInvoke, as also its equivalents in languages like Python, is absolutely evil.

Proposed ways:

  1. if you can affort an unmanaged DLL - then write a C function in it. Its interface must be designed to be pInvoke-friendly, and its implementation is yes, correct, the IOCTLs you are calling. Calling them from unmanaged code is much easier.
  2. if you must have a C#-only solution - invoke the PowerShell cmdlets from C#, or reverse engineer their .cdxml files and look at the WMI/WinRM stuff they are based on. Then invoke that WMI/WinRM stuff from C#, which is trivial and elegant.

Also - why are you querying the CHS values? for alignment?


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

wrote in message news:xxxxx@ntdev…
> Iam trying to use DeviceIOControl to create multiple partiions in USB. It is always creating only one partition.
>
> Here is my source code
>
> [DllImport(“kernel32.dll”, SetLastError = true)]
> static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
> uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
> uint dwFlagsAndAttributes, IntPtr hTemplateFile);
>
> [DllImport(“kernel32”)]
> static extern int CloseHandle(IntPtr handle);
>
>
> [DllImport(“kernel32”)]
> private static extern int DeviceIoControl
> (IntPtr deviceHandle, uint ioControlCode,
> IntPtr inBuffer, int inBufferSize,
> IntPtr outBuffer, int outBufferSize,
> ref int bytesReturned, IntPtr overlapped);
>
> public const uint GENERIC_READ = 0x80000000;
> public const uint GENERIC_WRITE = 0x40000000;
> public const uint FILE_SHARE_READ = 0x00000001;
> public const uint FILE_SHARE_WRITE = 0x00000002;
> public const uint OPEN_EXISTING = 0x00000003;
> public const uint FILE_ATTRIBUTE_NORMAL = 0x80;
> public const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x90083;
> public const int DRIVE_ACCESS_RETRIES = 10;
> public const int DRIVE_ACCESS_TIMEOUT = 15000;
> public const uint FSCTL_LOCK_VOLUME = 0x00090018;
> static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
> public const uint IOCTL_DISK_GET_DRIVE_LAYOUT_EX = 0x00070050;
> public const uint IOCTL_DISK_GET_DRIVE_GEOMETRY_EX = 0x000700A0;
> public const uint IOCTL_DISK_CREATE_DISK = 0x0007C058;
> public const uint IOCTL_DISK_UPDATE_PROPERTIES = 0x00070140;
> public const uint IOCTL_DISK_SET_DRIVE_LAYOUT_EX = 0x0007C054;
> public const int MIN_EXTRA_PART_SIZE = 1024 * 1024;
> public static int mediaType = 0;
>
>
> ///


> /// Describes the geometry of disk devices and media.
> ///

> [StructLayout(LayoutKind.Explicit)]
> public struct DISK_GEOMETRY
> {
> ///
> /// The number of cylinders.
> ///

> [FieldOffset(0)]
> public Int64 Cylinders;
>
> ///
> /// The type of media. For a list of values, see MEDIA_TYPE.
> ///

> [FieldOffset(8)]
> public MEDIA_TYPE MediaType;
>
> ///
> /// The number of tracks per cylinder.
> ///

> [FieldOffset(12)]
> public uint TracksPerCylinder;
>
> ///
> /// The number of sectors per track.
> ///

> [FieldOffset(16)]
> public uint SectorsPerTrack;
>
> ///
> /// The number of bytes per sector.
> ///

> [FieldOffset(20)]
> public uint BytesPerSector;
> }
>
>
>
> ///
> /// Describes the extended geometry of disk devices and media.
> ///

> [StructLayout(LayoutKind.Explicit)]
> private struct DISK_GEOMETRY_EX
> {
> ///
> /// A DISK_GEOMETRY structure.
> ///

> [FieldOffset(0)]
> public DISK_GEOMETRY Geometry;
>
> ///
> /// The disk size, in bytes.
> ///

> [FieldOffset(24)]
> public Int64 DiskSize;
>
> ///
> /// Any additional data.
> ///

> [FieldOffset(32)]
> public Byte Data;
> }
>
>
>
> ///
> /// Represents the format of a partition.
> ///

> public enum PARTITION_STYLE : uint
> {
> ///
> /// Master boot record (MBR) format.
> ///

> PARTITION_STYLE_MBR = 0,
>
> ///
> /// GUID Partition Table (GPT) format.
> ///

> PARTITION_STYLE_GPT = 1,
>
> ///
> /// Partition not formatted in either of the recognized formats?MBR or GPT.
> ///

> PARTITION_STYLE_RAW = 2
> }
>
> ///
> /// Contains partition information specific to master boot record (MBR) disks.
> ///

> [StructLayout(LayoutKind.Explicit)]
> public struct PARTITION_INFORMATION_MBR
> {
> #region Constants
> ///
> /// An unused entry partition.
> ///

> public const byte PARTITION_ENTRY_UNUSED = 0x00;
>
> ///
> /// A FAT12 file system partition.
> ///

> public const byte PARTITION_FAT_12 = 0x01;
>
> ///
> /// A FAT16 file system partition.
> ///

> public const byte PARTITION_FAT_16 = 0x04;
>
> ///
> /// An extended partition.
> ///

> public const byte PARTITION_EXTENDED = 0x05;
>
> ///
> /// An IFS partition.
> ///

> public const byte PARTITION_IFS = 0x07;
>
> ///
> /// A FAT32 file system partition.
> ///

> public const byte PARTITION_FAT32 = 0x0B;
>
> ///
> /// A logical disk manager (LDM) partition.
> ///

> public const byte PARTITION_LDM = 0x42;
>
> ///
> /// An NTFT partition.
> ///

> public const byte PARTITION_NTFT = 0x80;
>
> ///
> /// A valid NTFT partition.
> ///
> /// The high bit of a partition type code indicates that a partition is part of an NTFT mirror or striped array.
> ///

> public const byte PARTITION_VALID_NTFT = 0xC0;
> #endregion
>
> ///
> /// The type of partition. For a list of values, see Disk Partition Types.
> ///

> [FieldOffset(0)]
> [MarshalAs(UnmanagedType.U1)]
> public byte PartitionType;
>
> ///
> /// If this member is TRUE, the partition is bootable.
> ///

> [FieldOffset(1)]
> [MarshalAs(UnmanagedType.I1)]
> public bool BootIndicator;
>
> ///
> /// If this member is TRUE, the partition is of a recognized type.
> ///

> [FieldOffset(2)]
> [MarshalAs(UnmanagedType.I1)]
> public bool RecognizedPartition;
>
> ///
> /// The number of hidden sectors in the partition.
> ///

> [FieldOffset(4)]
> public uint HiddenSectors;
> }
>
> ///
> /// Contains GUID partition table (GPT) partition information.
> ///

> [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
> public struct PARTITION_INFORMATION_GPT
> {
> ///
> /// A GUID that identifies the partition type.
> ///
> /// Each partition type that the EFI specification supports is identified by its own GUID, which is
> /// published by the developer of the partition.
> ///

> [FieldOffset(0)]
> public Guid PartitionType;
>
> ///
> /// The GUID of the partition.
> ///

> [FieldOffset(16)]
> public Guid PartitionId;
>
> ///
> /// The Extensible Firmware Interface (EFI) attributes of the partition.
> ///
> ///

> [FieldOffset(32)]
> public UInt64 Attributes;
>
> ///
> /// A wide-character string that describes the partition.
> ///

> [FieldOffset(40)]
> public string Name;
> }
> ///
> /// Provides information about a drive’s master boot record (MBR) partitions.
> ///

> [StructLayout(LayoutKind.Explicit)]
> private struct DRIVE_LAYOUT_INFORMATION_MBR
> {
> ///
> /// The signature of the drive.
> ///

> [FieldOffset(0)]
> public uint Signature;
> }
>
> ///
> /// Contains information about a drive’s GUID partition table (GPT) partitions.
> ///

> [StructLayout(LayoutKind.Explicit)]
> private struct DRIVE_LAYOUT_INFORMATION_GPT
> {
> ///
> /// The GUID of the disk.
> ///

> [FieldOffset(0)]
> public Guid DiskId;
>
> ///
> /// The starting byte offset of the first usable block.
> ///

> [FieldOffset(16)]
> public Int64 StartingUsableOffset;
>
> ///
> /// The size of the usable blocks on the disk, in bytes.
> ///

> [FieldOffset(24)]
> public Int64 UsableLength;
>
> ///
> /// The maximum number of partitions that can be defined in the usable block.
> ///

> [FieldOffset(32)]
> public uint MaxPartitionCount;
> }
>
>
> ///
> /// Contains information about a disk partition.
> ///

> [StructLayout(LayoutKind.Explicit)]
> public struct PARTITION_INFORMATION_EX
> {
> ///
> /// The format of the partition. For a list of values, see PARTITION_STYLE.
> ///

> [FieldOffset(0)]
> public PARTITION_STYLE PartitionStyle;
>
> ///
> /// The starting offset of the partition.
> ///

> [FieldOffset(8)]
> public Int64 StartingOffset;
>
> ///
> /// The length of the partition, in bytes.
> ///

> [FieldOffset(16)]
> public Int64 PartitionLength;
>
> ///
> /// The number of the partition (1-based).
> ///

> [FieldOffset(24)]
> public uint PartitionNumber;
>
> ///
> /// If this member is TRUE, the partition information has changed. When you change a partition (with
> /// IOCTL_DISK_SET_DRIVE_LAYOUT), the system uses this member to determine which partitions have changed
> /// and need their information rewritten.
> ///

> [FieldOffset(28)]
> [MarshalAs(UnmanagedType.I1)]
> public bool RewritePartition;
>
> ///
> /// A PARTITION_INFORMATION_MBR structure that specifies partition information specific to master boot
> /// record (MBR) disks. The MBR partition format is the standard AT-style format.
> ///

> [FieldOffset(32)]
> public PARTITION_INFORMATION_MBR Mbr;
>
> ///
> /// A PARTITION_INFORMATION_GPT structure that specifies partition information specific to GUID partition
> /// table (GPT) disks. The GPT format corresponds to the EFI partition format.
> ///

> [FieldOffset(32)]
> public PARTITION_INFORMATION_GPT Gpt;
> }
>
>
> [StructLayout(LayoutKind.Explicit)]
> private struct DRIVE_LAYOUT_INFORMATION_UNION
> {
> [FieldOffset(0)]
> public DRIVE_LAYOUT_INFORMATION_MBR Mbr;
>
> [FieldOffset(0)]
> public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
> }
>
> ///
> /// Contains extended information about a drive’s partitions.
> ///

> [StructLayout(LayoutKind.Explicit)]
> private struct DRIVE_LAYOUT_INFORMATION_EX
> {
> ///
> /// The style of the partitions on the drive enumerated by the PARTITION_STYLE enumeration.
> ///

> [FieldOffset(0)]
> public PARTITION_STYLE PartitionStyle;
>
> ///
> /// The number of partitions on a drive.
> ///
> /// On disks with the MBR layout, this value is always a multiple of 4. Any partitions that are unused have
> /// a partition type of PARTITION_ENTRY_UNUSED.
> ///

> [FieldOffset(4)]
> public uint PartitionCount;
>
> ///
> /// A DRIVE_LAYOUT_INFORMATION_MBR structure containing information about the master boot record type
> /// partitioning on the drive.
> ///

> [FieldOffset(8)]
> public DRIVE_LAYOUT_INFORMATION_UNION Mbr;
>
> ///
> /// A DRIVE_LAYOUT_INFORMATION_GPT structure containing information about the GUID disk partition type
> /// partitioning on the drive.
> ///

> // [FieldOffset(8)]
> //public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
>
> ///
> /// A variable-sized array of PARTITION_INFORMATION_EX structures, one structure for each partition on the
> /// drive.
> ///

> [FieldOffset(48)]
> [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 4)]
> public PARTITION_INFORMATION_EX PartitionEntry;
>
> }
>
> [StructLayout(LayoutKind.Explicit)]
> private struct CREATE_DISK_MBR
> {
> [FieldOffset(0)]
> public uint Signature;
> }
>
> [StructLayout(LayoutKind.Explicit)]
> private struct CREATE_DISK_GPT
> {
> [FieldOffset(0)]
> public Guid DiskId;
>
> [FieldOffset(16)]
> public uint MaxPartitionCount;
> }
>
> [StructLayout(LayoutKind.Explicit)]
> private struct CREATE_DISK
> {
> [FieldOffset(0)]
> public PARTITION_STYLE PartitionStyle;
>
> [FieldOffset(4)]
> public CREATE_DISK_MBR Mbr;
>
> [FieldOffset(4)]
> public CREATE_DISK_GPT Gpt;
> }
>
>
> static IntPtr GetHandle(int driveIndex)
> {
> IntPtr handle;
> //bool locked = false;
>
> Program p = new Program();
> string physicalName = p.GetPhysicalName(driveIndex);
>
> Debug.WriteLine(physicalName);
>
> handle = CreateFile(physicalName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
> IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
>
> if (handle == INVALID_HANDLE_VALUE)
> {
> Debug.WriteLine(Marshal.GetLastWin32Error());
> return IntPtr.Zero;
> }
> return handle;
> }
>
> //Returns true if drive Index is successfully created
> //Returns false if not created successfully
> static bool CreatePartition(int driveIndex)
> {
> IntPtr handle = GetHandle(driveIndex);
>
> if (handle == INVALID_HANDLE_VALUE)
> {
> return false;
> }
>
> //Step 2: IOCTL_DISK_GET_DRIVE_GEOMETRY_EX to get the physical disk’s geometry ( we need some information in it to fill partition data)
> //The number of surfaces (or heads, which is the same thing), cylinders, and sectors vary a lot; the specification of the number of each is called the geometry of a hard disk.
> //The geometry is usually stored in a special, battery-powered memory location called the CMOS RAM , from where the operating system can fetch it during bootup or driver initialization.
> int size = 0;
> DISK_GEOMETRY_EX geometry = new DISK_GEOMETRY_EX();
> IntPtr lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DISK_GEOMETRY_EX)));
> Marshal.StructureToPtr(geometry, lpOutBuffer, false);
> int result = DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, IntPtr.Zero, 0, lpOutBuffer,
> Marshal.SizeOf(typeof(DISK_GEOMETRY_EX)),
> ref size, IntPtr.Zero);
> geometry = (DISK_GEOMETRY_EX)Marshal.PtrToStructure(lpOutBuffer, typeof(DISK_GEOMETRY_EX));
>
>
> //Step 3: IOCTL_DISK_CREATE_DISK is used to initialize a disk with an empty partition table.
> CREATE_DISK createDisk = new CREATE_DISK();
> createDisk.PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
> createDisk.Mbr.Signature = 1;
>
> IntPtr createDiskBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CREATE_DISK)));
> Marshal.StructureToPtr(createDisk, createDiskBuffer, false);
>
> byte arr1 = new byte[Marshal.SizeOf(typeof(CREATE_DISK))];
> Marshal.Copy(createDiskBuffer, arr1, 0, Marshal.SizeOf(typeof(CREATE_DISK)));
>
> result = DeviceIoControl(handle, IOCTL_DISK_CREATE_DISK, createDiskBuffer, Marshal.SizeOf(typeof(CREATE_DISK)),
> IntPtr.Zero, 0, ref size, IntPtr.Zero);
>
> result = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, ref size, IntPtr.Zero);
>
>
> //Step 4: IOCTL_DISK_SET_DRIVE_LAYOUT_EX to repartition a disk as specified.
> //Note: use IOCTL_DISK_UPDATE_PROPERTIES to synchronize system view after IOCTL_DISK_CREATE_DISK and IOCTL_DISK_SET_DRIVE_LAYOUT_EX
> /* DWORD driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX) * 4 * 25;
> DRIVE_LAYOUT_INFORMATION_EX *DriveLayoutEx = (DRIVE_LAYOUT_INFORMATION_EX ) new BYTE[driveLayoutSize];/
>
>
>
> IntPtr driveLayoutbuffer = Marshal.AllocHGlobal(624);
> DRIVE_LAYOUT_INFORMATION_EX driveLayoutEx = new DRIVE_LAYOUT_INFORMATION_EX();
> int pn = 0;
> driveLayoutEx.PartitionEntry = new PARTITION_INFORMATION_EX[4];
>
>
> mediaType = (int)geometry.Geometry.MediaType;
> Int64 bytes_per_track = (geometry.Geometry.SectorsPerTrack) * (geometry.Geometry.BytesPerSector);
>
> driveLayoutEx.PartitionEntry[pn].StartingOffset = 0x123;
>
> Int64 main_part_size_in_sectors, extra_part_size_in_sectors = 0;
> main_part_size_in_sectors = (geometry.DiskSize - driveLayoutEx.PartitionEntry[pn].StartingOffset) / geometry.Geometry.BytesPerSector;
>
> if (main_part_size_in_sectors <= 0)
> {
> return false;
> }
>
> extra_part_size_in_sectors = (MIN_EXTRA_PART_SIZE + bytes_per_track - 1) / bytes_per_track;
> main_part_size_in_sectors = ((main_part_size_in_sectors / geometry.Geometry.SectorsPerTrack) -
> extra_part_size_in_sectors) * geometry.Geometry.SectorsPerTrack;
> if (main_part_size_in_sectors <= 0)
> {
> return false;
> }
>
> driveLayoutEx.PartitionEntry[pn].PartitionLength = 50000;
> driveLayoutEx.PartitionEntry[pn].PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
> driveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x07;
> driveLayoutEx.PartitionEntry[pn].Mbr.BootIndicator = true;
>
> pn++;
>
> // Set the optional extra partition
> // Should end on a track boundary
> driveLayoutEx.PartitionEntry[pn].StartingOffset = 0x400;
> driveLayoutEx.PartitionEntry[pn].PartitionLength = 26244; //TODO: Has to change
> driveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0xef;
>
> pn++;
>
> for (uint i = 0; i < pn; i++)
> {
> driveLayoutEx.PartitionEntry[i].PartitionNumber = i + 1;
> driveLayoutEx.PartitionEntry[i].PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
> driveLayoutEx.PartitionEntry[i].RewritePartition = true;
> }
>
> driveLayoutEx.PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
> driveLayoutEx.PartitionCount = 4; //It should be a multiple of 4
> driveLayoutEx.Mbr.Mbr.Signature = createDisk.Mbr.Signature;
>
>
>
>
> Marshal.StructureToPtr(driveLayoutEx, driveLayoutbuffer, false);
>
>
> result = DeviceIoControl(handle, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, driveLayoutbuffer, 624, IntPtr.Zero, 0, ref size, IntPtr.Zero);
> result = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, ref size, IntPtr.Zero);
>
> Marshal.FreeHGlobal(driveLayoutbuffer);
> Marshal.FreeHGlobal(createDiskBuffer);
>
> Marshal.FreeHGlobal(lpOutBuffer);
>
> return true;
> }
>
>
> It only creates one partition with offset to zero and partition length to full size of USB.
>
> Iam trying this from past two days but still no solution.
>
> Please help
>
>

> -----Original Message-----

From: xxxxx@lists.osr.com [mailto:bounce-603810-
xxxxx@lists.osr.com] On Behalf Of Maxim S. Shatskih
Sent: Thursday, March 03, 2016 10:42 PM
To: Windows System Software Devs Interest List
> Subject: Re:[ntdev] Creating multiple partitions on USB using C#
>
> Oh my God, this is EXACTLY the place where managed code makes the
> solution much worse.
>
> Managed code and C# should simplify the solution, not make it full
> of pInvoke scum, where it is too easy to make a bug and too hard to find
> it.
>
> pInvoke, as also its equivalents in languages like Python, is
> absolutely evil.
>
> Proposed ways:
>
> 1) if you can affort an unmanaged DLL - then write a C function in it.
> Its interface must be designed to be pInvoke-friendly, and its
> implementation is yes, correct, the IOCTLs you are calling. Calling them
> from unmanaged code is much easier.
> 2) if you must have a C#-only solution - invoke the PowerShell cmdlets
> from C#, or reverse engineer their .cdxml files and look at the WMI/WinRM
> stuff they are based on. Then invoke that WMI/WinRM stuff from C#, which
> is trivial and elegant.

You can also write a managed C++ assembly, also known as C++/CLI. The C++ hooks up to the C library, and it’s very easy to expose managed objects that are easy to consume from C#.

Phil

Not speaking for LogRhythm
Phil Barila | Senior Software Engineer
720.881.5364 (w)

LogRhythm, Inc?
A LEADER in Gartner’s SIEM Magic Quadrant four consecutive years (2012-2015)
Highest Score in Gartner’s 2015 SIEM Critical Capabilities Report
A CHAMPION in Info-Tech Research Group’s 2015 SIEM Vendor Landscape Report
SANS “Best of the Year” in SIEM, 2014
Perfect 5-Star Rating in SC Magazine (2009-2014)

???

Hold on, guys - does Windows support multiple partitions on the removable media,in the first place???

The last Windows version that I had first-hand experience with (I think it was XP SP2) did not - in order to do what the OP tries to do one needed a lower filter to USBSTOR that cleared the ''RemovableMedia" bit of STORAGE_DEVICE_DESCRIPTOR structure returned upon IOCTL_STORAGE_QUERY_PROPERTY request. Have the things changed in the last 10 years?

Anton Bassov

> Hold on, guys - does Windows support multiple partitions on the removable media,in the first

place???

Yes, you’re correct.

The Windows disk stack will only expose partition #1 as a volume with a drive letter. The remaining partitions will be hidden.

But: sometimes you need an MBR on a USB flash, for some scenarios with bootable flash drives I think.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

I got the Solution.

It was because

  1. Marshal.AllocHGlobal allocated memory is not zero filled.

  2. PARTITION_INFORMATION_GPT structure name member filled is only has 8 bytes of memory allocated instead it requires 72 bytes.

> But: sometimes you need an MBR on a USB flash, for some scenarios with bootable flash

drives I think.

The OP may want to rewrite my “dummydisk” sample that I had published a decade ago as an example of direct hooking - all he has to do is to rewrite it as an upper USBSTOR filter that acts as a simple passthrough for everything, apart from IRP_MJ_DEVICE_CONTROL…

Anton Bassov