News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Low-level access to CD/DVD drives

Started by Ian_B, July 27, 2006, 09:28:25 AM

Previous topic - Next topic

Ian_B

OK, as it's a little quiet... I'll ask a few questions I was saving for later.

According to MSDN for GetDiskFreeSpaceEx:

Quote from: MSDNThe GetDiskFreeSpaceEx function returns 0 (zero) for lpTotalNumberOfFreeBytes and lpFreeBytesAvailable for all CD requests unless the disk is an unwritten CD in a CD-RW drive. [MY ITALICS]

In my understanding, if a CD or a DVD is multi-session, as they normally are, then it's still writable, so if partially written there must be a way of getting the amount of space left available to write on it. This comment from MSDN suggests that may not be an obvious or simple task. Does anyone have any idea how to confirm the remaining space on a partially written CD/DVD disk?

Second question, about low-level access. I am aware that there is a program which allows you to extract any readable data from a CD/DVD disk if the OS refuses to read the files in the normal way, perhaps because the FAT is unreadable. This is a means of last-ditch data recovery in case of partial dye "fade" in the medium. Can anyone point me in the direction of APIs which would allow such recovery, avoiding normal file API procedures?

Ian_B

Tedd

I would assume the windows functions will report there is never any free space left on a written cd. A hack way to get the remaining space would be to subtract the size of the written data from the total capacity :wink

For direct access, ASPI is generally used - I presume this will provide the necessary functionality.
No snowflake in an avalanche feels responsible.

Ian_B

Quote from: Tedd on July 27, 2006, 11:18:46 AMA hack way to get the remaining space would be to subtract the size of the written data from the total capacity :wink

And how would you get either of those values?  :eek   The closest API I saw to finding the size of a volume is DeviceIoControl with IOCTL_DISK_GET_LENGTH_INFO, which is useless to me as it is only available from WinXP up.

QuoteFor direct access, ASPI is generally used - I presume this will provide the necessary functionality.

OK, I can see from the fact that the entire Windows SDK contains exactly one passing reference to ASPI that this is not going to be a short investigation...  :'(

Ian_B

Ian_B

Hmmmmmm...  :dazzled:

Found a smattering of highly obscure low-level info, and even a copy of the ASPI SDK, so I have somewhere to start to research the data recovery problem. Still not sure it's going to help me with the apparently simpler problem of finding the quantity of available unwritten space on a CD/DVD disk.

But now there is an even more obvious problem - supposing I can use ASPI to query the available adapters and find a CD or DVD drive on a particular adapter with a particular ID/LUN, how can I convert that to check if it's the particular LOGICAL drive (ie. F: G: H: etc.) I want?  :eek   Is there anyone out there with ASPI programming expertise that can help?

Ian_B

evlncrn8

do you have to use aspi?

why not use spti, its available from 2k onwards, works via createfile \\.\x: (where x = cd drive letter), and accessable like a file (setfilepointer, readfile and so on),
and supports dioc commands, which you can use to get the disk geometry etc.. much much much easier than aspi

Ian_B

Thanks for that suggestion, but there's even less information available for "SPTI" than there is for ASPI...  :bdg  I was also hoping to put together a solution that might work for preNT versions of Windows too.

Rummaging around the SDK for anything related, though, it appears that I can use DeviceIOControl with IOCTL_DISK_GET_DRIVE_GEOMETRY to retrieve the full disk size (by multiplying all the returned values together) and that will work on WinNT4+. That's a start... but still don't know how to find the amount of used space, and there don't seem to be any other relevant IOCTL codes even for later Win versions.

EDIT: I realised that I do actually have a hack-type solution for the used-space problem, although it's not ideal or exact. I am already doing a recursion to find all files in a subtree and get their sizes, and for a root path that would effectively be all files on the disk. So at that point I will have a count of the total file data written, although subtracting that from the media size reported may not be exactly the same as the total amount of space still available.

Ian_B


Ian_B

#6
Related problem, an enum value returned in the DISK_GEOMETRY structure. I am interested in the RemovableMedia value if present... but what is the actual number? It's not explicitly defined in winioctl.h except for this C code. Does the enum start at 0 or 1, ie. is the RemovableMedia value 11 or 12?

typedef enum _MEDIA_TYPE
{
  Unknown    Format is unknown,
  F5_1Pt2_512    A 5.25" floppy, with 1.2MB and 512 bytes/sector,
  F3_1Pt44_512    A 3.5" floppy, with 1.44MB and 512 bytes/sector,
  F3_2Pt88_512    A 3.5" floppy, with 2.88MB and 512 bytes/sector,
  F3_20Pt8_512    A 3.5" floppy, with 20.8MB and 512 bytes/sector,
  F3_720_512    A 3.5" floppy, with 720KB and 512 bytes/sector,
  F5_360_512    A 5.25" floppy, with 360KB and 512 bytes/sector,
  F5_320_512    A 5.25" floppy, with 320KB and 512 bytes/sector,
  F5_320_1024    A 5.25" floppy, with 320KB and 1024 bytes/sector,
  F5_180_512    A 5.25" floppy, with 180KB and 512 bytes/sector,
  F5_160_512    A 5.25" floppy, with 160KB and 512 bytes/sector,
  RemovableMedia    Removable media other than floppy,
  FixedMedia    Fixed hard disk media,
  F3_120M_512    A 3.5" floppy, with 120MB and 512 bytes/sector,
  F3_640_512    A 3.5" floppy, with 640MB and 512 bytes/sector,
  F5_640_512    A 5.25" floppy, with 640KB and 512 bytes/sector,
  F5_720_512    A 5.25" floppy, with 720KB and 512 bytes/sector,
  F3_1Pt2_512    A 3.5" floppy, with 1.2MB and 512 bytes/sector,
  F3_1Pt23_1024    A 3.5" floppy, with 1.23MB and 1024 bytes/sector,
  F5_1Pt23_1024    A 5.25" floppy, with 1.23KB and 1024 bytes/sector,
  F3_128Mb_512    A 3.5" floppy, with 128MB and 512 bytes/sector,
  F3_230Mb_512    A 3.5" floppy, with 230MB and 512 bytes/sector,
  F8_256_128    An 8" floppy, with 256KB and 128 bytes/sector,
  F3_200Mb_512    A 3.5" floppy, with 200MB and 512 bytes/sector. (HiFD),
  F3_240M_512    A 3.5" floppy, with 240MB and 512 bytes/sector. (HiFD),
  F3_32M_512    A 3.5" floppy, with 32MB and 512 bytes/sector.
} MEDIA_TYPE;


EDIT: Guess it's easier to ignore this and just test for DRIVE_CDROM returned by GetDriveType...  :bg

EDIT 2: It's sad that no C-savvy programmer here bothered to reply.  :'(  Empirically, though, the enum starts at 0 and the RemovableMedia value is correctly returned as 11 for a CD drive using a IOCTL_DISK_GET_DRIVE_GEOMETRY call (see below).

Ian_B

Ian_B

Another issue I have just found with this general problem - to access a volume using DeviceIoControl requires the following:

Quote from: MSDNPhysical Disks and Volumes

You can use the CreateFile function to open a physical disk drive or a volume. The function returns a handle that can be used with the DeviceIoControl function. This enables you to access the disk partition table. However, it is potentially dangerous to do so, because an incorrect write to a disk could make its contents inaccessible. The following requirements must be met for such a call to succeed:

The caller must have administrative privileges.

So it seems you can't use this method to simply query a CD/DVD drive and find out the maximum size of the media it currently contains, unless you are running in admin privilege mode. How crazy is that? There HAS to be another method that works for all privilege levels, surely?  :eek  Unless we are back to ASPI...

Ian_B

Ian_B

Here's a minimal piece of code to answer some of these questions. The results initially seemed encouraging, except for the last mentioned issue of the CreateFile call for a disk volume only working at administrative privilege level...

.686
.MMX
.model flat, stdcall
option casemap:none
option noscoped

include masm32\include\windows.inc
include masm32\include\kernel32.inc
include masm32\include\masm32.inc

include masm32\macros\macros.asm
include masm32\macros\timers.asm

includelib masm32\lib\kernel32.lib
includelib masm32\lib\masm32.lib


Main proto


.data

DISK_GEOMETRY struct
  CylindersLo               DWORD ?
  CylindersHi               DWORD ?
  MediaType                 DWORD ?
  TracksPerCylinder         DWORD ?
  SectorsPerTrack           DWORD ?
  BytesPerSector            DWORD ?
DISK_GEOMETRY ENDS

  IOCTL_DISK_GET_DRIVE_GEOMETRY     EQU 70000H
  SzCDDrive                         db "\\.\R:",0


.data?

align 16

  DG            DISK_GEOMETRY <>
  junk          DWORD ?

.code

Start:
Invoke Main
Invoke ExitProcess, 0

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

align 16

Main    proc

        push    ebx
        push    edi
        push    esi

        mov     eax, input("Press ENTER to begin...",13,10,13,10)
        xor     ebx, ebx
        mov     ecx, FILE_SHARE_READ+FILE_SHARE_WRITE    ; must include FILE_SHARE_WRITE
        invoke  CreateFile, OFFSET SzCDDrive, ebx , ecx, ebx, OPEN_EXISTING, ebx, ebx
        mov     esi, eax
        cmp     eax, INVALID_HANDLE_VALUE
        je      AccessError

        lea     edi, DG
        mov     edx, IOCTL_DISK_GET_DRIVE_GEOMETRY
        invoke  DeviceIoControl, esi, edx, ebx, ebx, edi, SIZEOF DISK_GEOMETRY, OFFSET junk, ebx
        test    eax, eax
        jz      AccessError

        ASSUME EDI:PTR DISK_GEOMETRY
        print   chr$("Drive R:",13,10,"CylindersLo: ")
        mov     eax, [edi].CylindersLo
        print   ustr$(eax)
        print   chr$(13,10,"CylindersHi: ")
        mov     eax, [edi].CylindersHi
        print   ustr$(eax)
        print   chr$(13,10,"MediaType: ")
        mov     eax, [edi].MediaType
        print   ustr$(eax)
        print   chr$(13,10,"TracksPerCylinder: ")
        mov     eax, [edi].TracksPerCylinder
        print   ustr$(eax)
        print   chr$(13,10,"SectorsPerTrack: ")
        mov     eax, [edi].SectorsPerTrack
        print   ustr$(eax)
        print   chr$(13,10,"BytesPerSector: ")
        mov     eax, [edi].BytesPerSector
        print   ustr$(eax)
        ASSUME EDI:NOTHING
        jmp     @F

AccessError:
        invoke  GetLastError
        mov     ebx, eax
        print   chr$("I/O Error:")
        print   ustr$(ebx)
@@:
        print   chr$(13,10,13,10)
        mov     eax, input("Press ENTER to exit...")
        pop     esi
        pop     edi
        pop     ebx
        ret

Main EndP

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

End Start


The output for my CD-writer:

Drive R:
CylindersLo: 163
CylindersHi: 0
MediaType: 11
TracksPerCylinder: 64
SectorsPerTrack: 32
BytesPerSector: 2048


So at least we find the answer to the enum query above, and that the size of a MEDIA_TYPE entry (which I have unsuccessfully searched high and low for on the net and through the SDK all day) is a DWORD, so the structure definition is valid...

However, multiplying up all the values gives a result of 652Mb for a pre-pressed CD (above), and 613Mb for a "700Mb" CD-RW I tested with 613Mb of data on it (as reported by Explorer, which also correctly said there was 67.1Mb still available to write), so by reporting the used space rather than the overall drive geometry as might have been expected it's doing no better than the count-all-the-files approach, and with the limitation of the admin privilege required. And the Explorer info shows there's definitely a method of getting the full media size that doesn't require ASPI.

So what the **** is it?  :dazzled:

Ian_B

evlncrn8

actually, i'd still use the createfile \\.\*: (* = cd/dvd letter), and you stated it needs admin, well it does
but theres a little 'backdoor'...

QueryDosDevice on the drive letter, it returns sz terminated strings, with 'different' access models (backdoorish)
you'll see something like Device\Cdrom0 etc...

CreateFile on \\.\Cdrom0 works (ie: one of the 'models' returned in the buffer.. stripped...cant remember if you need a : at the end..)
works in NON admin mode...

from then you have your interface, then you can do all the DeviceIoControl you like ;)

hope that helps (and again, only works on 2k upwards..)

oh yeah, and as for the size.. tried using GetFileSize on the handle you get from the CreateFile ? hmm, just checked, that doesnt work, how about GetDiskFreeSpace or something? i'd use the ioctl personally

and for sector reading, SetFilePointer (sectorsize*sector you want to read) then ReadFile works :)

Evenbit

Quote from: Ian_B on July 27, 2006, 09:28:25 AM
OK, as it's a little quiet... I'll ask a few questions I was saving for later.

According to MSDN for GetDiskFreeSpaceEx:

Quote from: MSDNThe GetDiskFreeSpaceEx function returns 0 (zero) for lpTotalNumberOfFreeBytes and lpFreeBytesAvailable for all CD requests unless the disk is an unwritten CD in a CD-RW drive. [MY ITALICS]

In my understanding, if a CD or a DVD is multi-session, as they normally are, then it's still writable, so if partially written there must be a way of getting the amount of space left available to write on it. This comment from MSDN suggests that may not be an obvious or simple task. Does anyone have any idea how to confirm the remaining space on a partially written CD/DVD disk?

Second question, about low-level access. I am aware that there is a program which allows you to extract any readable data from a CD/DVD disk if the OS refuses to read the files in the normal way, perhaps because the FAT is unreadable. This is a means of last-ditch data recovery in case of partial dye "fade" in the medium. Can anyone point me in the direction of APIs which would allow such recovery, avoiding normal file API procedures?

Ian_B


Did you test the 'GetDiskFreeSpace' function??

evlncrn8

think it'd be the same as above - spti interface, using raw reads, not 'cooked' (different IOCTL code for that)