: Getting all partitions on a Drive using DeviceIoControl

>I am trying to get details on all the partitions on a physical drive, but I

am getting an access denied message with the call to DeviceIOControl.

hDevice := CreateFile(PChar(Drive),0, FILE_SHARE_READ Or
FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

try asking for read permission

hDevice := CreateFile(PChar(Drive), GENERIC_READ, FILE_SHARE_READ Or
FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

If that fails, which I doubt, try
GENERIC_READ | GENERIC_WRITE
and/or FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
and/or FILE_ATTRIBUTE_NORMAL (I’ve always found the docs on that parameter
confusing, it sounds like FILE_ATTRIBUTE_NORMAL is required, if nothing
else, yet people seem to get away with 0.)

  • Jay

You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com

>From: xxxxx@cornell.edu [mailto:xxxxx@cornell.edu]

Subject: : Getting all partitions on a Drive using DeviceIoControl

>I am trying to get details on all the partitions on a physical drive, but
I
>am getting an access denied message with the call to DeviceIOControl.

> hDevice := CreateFile(PChar(Drive),0, FILE_SHARE_READ Or
>FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

try asking for read permission

> hDevice := CreateFile(PChar(Drive), GENERIC_READ, FILE_SHARE_READ Or
>FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

If that fails, which I doubt, try
GENERIC_READ | GENERIC_WRITE
and/or FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
and/or FILE_ATTRIBUTE_NORMAL (I’ve always found the docs on that parameter
confusing, it sounds like FILE_ATTRIBUTE_NORMAL is required, if nothing
else, yet people seem to get away with 0.)

  • Jay
    -----Original Message-----
    From: Chris Crowe
    >Subject: RE: : Getting all partitions on a Drive using DeviceIoControl

    >Hi Jay,
    >
    >Thanks for the info there. I tried the GENERIC_READ and got a different
    >error message which was 122.
    >
    >“The data area passed to a system call is too small.”
    >
    >So it means my data structure is wrong…
    >
    >A couple of questions:
    >
    >1) Am I suppose to use
    >
    >Drive := ‘\.\PHYSICALDRIVE0’; // This is what I am using!!!
    >or
    >Drive := ‘\.\c:’;
    >
    >2) From the docs I have I am suppose to send a DRIVE_LAYOUT_INFORMATION
    >structure, but I do not understand how a structure with an array of 1 can
    be
    >variable sized.
    >
    >This is what I have written :
    >
    >Type DRIVE_LAYOUT_INFORMATION = Record
    > PartitionCount : DWORD;
    > Signature : DWORD;
    > Partitioninfo: array[0…1] of PARTITION_INFORMATION;
    > end;
    >
    >This is what the docs say:
    >
    >typedef struct _DRIVE_LAYOUT_INFORMATION
    >
    > DWORD PartitionCount;
    > DWORD Signature;
    > PARTITION_INFORMATION PartitionEntry[1];
    >} DRIVE_LAYOUT_INFORMATION;
    >
    >Any way I do not get a valid partition count back ever.
    >
    >So I have ended up with this code at the moment.
    >
    >Var // Declare Variables
    > RetBytes : DWORD;
    > hDevice : Longint;
    > Status : Longbool;
    > Drive : String;
    > PartitionInfo : DRIVE_LAYOUT_INFORMATION;
    >begin
    > Drive := ‘\.\PHYSICALDRIVE’ + inttostr(DriveNum);
    > hDevice := CreateFile(PChar(Drive),GENERIC_READ, FILE_SHARE_READ Or
    >FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
    > If hDevice <> INVALID_HANDLE_VALUE Then
    > begin
    > Status := DeviceIoControl (hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT,
    >nil, 0, @PartitionInfo,
    > Sizeof(DRIVE_LAYOUT_INFORMATION), RetBytes, nil);
    > if (status = false) then
    > showmessage(‘Read Partitions Failed :
    ’+inttostr(getLastError()));
    > CloseHandle(hDevice);
    > end;
    >End;
    >
    >Your help would be most appreciated, the RetBytes returns a value of
    1244448
    >when the status = false, and the error code is 122 which is “The data area
    >passed to a system call is too small.” (when using my original structure
    >above)
    >
    >if I change the structure to the following
    >
    >Type DRIVE_LAYOUT_INFORMATION = Record
    > PartitionCount : DWORD;
    > Signature : DWORD;
    > Partitioninfo: array[0…100] of PARTITION_INFORMATION;
    > end;
    >
    >so that I can have 100 partitions I do not get an error but I get a count
    of
    >8 partitions, but I only have 1 physical hard drive, and 2 partitions.
    >
    >The retBytes drops to 264 bytes, but from what I can see each
    >PARTITION_INFORMATION structure is only 32 bytes long.
    >
    >So from this It looks like the DRIVE_LAYOUT_INFORMATION is correct (sort
    of)
    >
    >4 bytes for the PartitionCount, 4 bytes for the Signature, and 8 x
    >PARTITION_INFORMATION structures @ 32 bytes each which equates to 264
    bytes.
    >
    >Please help…
    >
    >Chris

    Chris, You’d /really/ be better off using C or C++ and the Microsoft
    headers, or even Microsoft Java, C#, or script, IF IF they provide
    declarations for these things (I doubt they do, if only they were in winnt.h
    instead of winioctl.h…). How do you even know if you get the right
    “packing/alignment” of these structs/records correct? Maybe that’s a
    relatively solved issue in Delphi. I don’t think I’ve seen any Pascal/Delphi
    on this list before.

    Whatever the language arguments, the “library binding” argument very very
    strongly favors C/C++ imho.

    It’s a somewhat common practise to have structs end in variably sized
    arrays, 0 or 1 or unstated, where only 1 is really allowed by the C
    standard, and the general practise isn’t theoretically portable, but is
    practically portable.

    struct FooElement
    {
    int blah;
    char blahblah;
    };

    struct Foo
    {
    int count;
    FooElement foos[1];
    // aka FooElement foos[0]; but I think this is much less portable, not
    even accepted by Visual C++
    // aka FooElement foos; but I think this is much less portable as well,
    not even accepted by Visual C++
    // aka FooElement foos[]; at least for Midl, as seen in winnt.h
    // notice in winnt.h: #define ANYSIZE_ARRAY 1, and look for its uses, mostly
    arrays at the ends of structs
    };

    char buffer[sizeof(Foo) + 100
    sizeof(FooElement)];
    Foo* pfoo = (Foo*)&buffer;
    for (i = 0 ; i < pfoo->count ; ++i)
    {
    pfoo->foos[i] …;
    }

    It is a bit dubious from a strongly typed point of view, but it is
    efficient. It’s a bit too bad there’s no portable safe way to do this…one
    that would pass a C/C++ compiler/codegen that did array range checking…
    (some do, at least optionally).

    DeviceIoControl(&pfoo, sizeof(buffer), …);

    I think you want PhysicalDrive0.

    Be sure to get winobj.exe from http://www.sysinternals.com and look around
    under ?? and \Device\Harddisk* to get another measure of the count of
    partitions (and notice the differences between NT4 and Win2k). I think
    there’s always the zeroeth partition that includes the whole drive. It is
    interesting in general to browse the NT namespace.

    >PARTITION_INFORMATION structure is only 32 bytes long.

    Seems about right. 28 bytes, then padded/aligned.

    Send questions to the mailing list, not to me directly. Thanks.

    - Jay


    You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
    To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com