Ace128 has asked for the wisdom of the Perl Monks concerning the following question:

Greetings Monks,

Im working on getting where files exists physicaly (raw) on disk (sector). But Im having some trouble getting the Windows things to work in Perl... I thought this would be easy, following PodMaster's nicely Win32::ReadDirectoryChanges module, but apparently not. However, this may be some windows specific thing, that I need to know to make it work. Im using Win32::API and have the following code:
# #!/usr/bin/env perl use Win32::API; use Win32API::File qw /:Func :IOCTL_DISK_/; use Data::Dumper; use strict; use warnings; #use diagnostics; # HANDLE CreateFile( # LPCTSTR lpFileName, # DWORD dwDesiredAccess, # DWORD dwShareMode, # LPSECURITY_ATTRIBUTES lpSecurityAttributes, # DWORD dwCreationDisposition, # DWORD dwFlagsAndAttributes, # HANDLE hTemplateFile # ); # BOOL DeviceIoControl( # (HANDLE) hDevice, // handle to volume # IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, // dwIoControlCode # (LPVOID) lpInBuffer, // input buffer # (DWORD) nInBufferSize, // size of input buffer # (LPVOID) lpOutBuffer, // output buffer # (DWORD) nOutBufferSize, // size of output buffer # (LPDWORD) lpBytesReturned, // number of bytes returned # (LPOVERLAPPED) lpOverlapped // OVERLAPPED structure # ); # typedef struct _VOLUME_DISK_EXTENTS { # DWORD NumberOfDiskExtents; # DISK_EXTENT Extents[1]; # } VOLUME_DISK_EXTENTS, # *PVOLUME_DISK_EXTENTS; # typedef struct _DISK_EXTENT { # DWORD DiskNumber; # LARGE_INTEGER StartingOffset; # LARGE_INTEGER ExtentLength; # } DISK_EXTENT, # *PDISK_EXTENT; # DWORD = N ## Internal stuff my ($CloseHandle, $GetCurrentProcess, $OpenProcessToken, $LookupPrivilegeValue, $AdjustTokenPrivileges, $CreateFile, $ReadDirectoryChanges, $DeviceIoControl); sub FILE_LIST_DIRECTORY { 0x00000001 } sub FILE_SHARE_READ { 0x00000001 } sub FILE_SHARE_WRITE { 0x00000002 } sub OPEN_EXISTING { 3 } sub FILE_FLAG_BACKUP_SEMANTICS { 0x02000000 } sub IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS { 5636096 } sub FILE_READ_ATTRIBUTES { 0x0080 } sub FILE_NOTIFY_CHANGE_NAME { 0x00000003 } sub DWORD_SIZE { 4 } sub TOKEN_ADJUST_PRIVILEGES { 0x0020 } sub TOKEN_QUERY { 0x0008 } sub SE_BACKUP_NAME { 'SeBackupPrivilege' } sub SE_RESTORE_NAME { 'SeRestorePrivilege' } sub SE_CHANGE_NOTIFY_NAME { 'SeChangeNotifyPrivilege' } sub SE_PRIVILEGE_ENABLED { 2 } sub _InitializeAPI { my $kernel32 = 'kernel32.dll'; my $advapi32 = 'advapi32.dll'; $CloseHandle = new Win32::API($kernel32, 'CloseHandle', 'N', 'I') || die; $GetCurrentProcess = new Win32::API($kernel32, 'GetCurrentProcess', '', 'N') || die $^E; $OpenProcessToken = new Win32::API($advapi32, 'OpenProcessToken', 'NNP', 'I' ) || die $^E; $LookupPrivilegeValue = new Win32::API($advapi32, 'LookupPrivilegeValue', 'PPP', 'I') || die $^E; $AdjustTokenPrivileges = new Win32::API($advapi32, 'AdjustTokenPrivileges', 'NIPNPP', 'I') || die $^E; $CreateFile = new Win32::API($kernel32, 'CreateFileA', 'PNNPNNN', 'N') or die $^E; $ReadDirectoryChanges = new Win32::API($kernel32, 'ReadDirectoryChangesW', 'NPNINPPP', 'I') || die $^E +; $DeviceIoControl = new Win32::API($kernel32, 'DeviceIoControl', 'NPPNPNPP', 'I') || die $^E; } sub _EnablePrivileges { my $phToken = pack("L", 0); if($OpenProcessToken->Call($GetCurrentProcess->Call(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, $phToken)) { my $hToken = unpack("L", $phToken); _SetPrivilege($hToken, SE_BACKUP_NAME, 1); _SetPrivilege($hToken, SE_RESTORE_NAME, 1); _SetPrivilege($hToken, SE_CHANGE_NOTIFY_NAME, 1); $CloseHandle->Call($hToken); } #print "privileges altered\n"; } sub _SetPrivilege { my ($hToken, $pszPriv, $bSetFlag) = @_; my $iResult; my $pLuid = pack("Ll", 0, 0); if($LookupPrivilegeValue->Call("\x00\x00", $pszPriv, $pLuid)) { my $pPrivStruct = pack("LLlL", 1, unpack("Ll", $pLuid), (($bSetFlag)? SE_PRIVILEGE_ENABLED : 0) +); $iResult = (0 != $AdjustTokenPrivileges->Call($hToken, 0, $pPrivStruct, length($pPrivStr +uct), 0, 0)); } return ($iResult); } _InitializeAPI(); _EnablePrivileges(); my $hDevice; #my $dwIoControlCode = FSCTL_GET_RETRIEVAL_POINTERS; #my $lpInBuffer = pack("L", 0); my $lpInBuffer = []; my $nInBufferSize = 0; my $lpOutBuffer = 0; my $nOutBufferSize = 0; my $lpBytesReturned = 0; my $lpOverlapped = []; #$hDevice = createFile( "//./PhysicalDrive1", "r", "rw") # or die "Can't open: $^E\n"; #my $path = "//./D:/Snap1.gif"; #my $path = "D:/Snap1.gif"; my $path = "D:/"; $hDevice = $CreateFile->Call($path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_W +RITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0) || die $^E; my $nBufferLength = 1024; #1024 * DWORD_SIZE; my $pBuffer = "\x00" x $nBufferLength; my $pBytesReturned = pack("L", 0); # my $iResult = $ReadDirectoryChanges->Call($hDevice, # $pBuffer, # $nBufferLength, # 1, # FILE_NOTIFY_CHANGE_NAME +, # $pBytesReturned, # 0, 0) || die $^E; # my $bytesReturned = unpack("L", $pBytesReturned); #print Dumper($iResult); print Dumper($hDevice); #if ($iResult < 1) { # print Win32::GetLastError() . "\n"; # print $^E . "\n"; # exit; #} if ($hDevice < 1) { print $^E . "\n"; exit; } #$hDevice = unpack("L", $hDevice); #print $hDevice . "\n"; #Win32API::File::DeviceIoControl($hDevice, # IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, # 0, # [], # $lpOutBuffer, # $nOutBufferSize, # $lpBytesReturned, # 0, # ) || die $^E; my $iResult2 = $DeviceIoControl->Call($hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, 0, [], $lpOutBuffer, $nOutBufferSize, $lpBytesReturned, 0, ) || die $^E; print Dumper($lpOutBuffer); print Dumper($nOutBufferSize); my $pBytesReturned2 = pack("L", 0); my $bytesReturned2 = unpack("L", $pBytesReturned2); my @results = (); print $bytesReturned2 . "\n"; __END__ if ($bytesReturned > 0) { my ($NextEntryOffset, $Action, $FileNameLength, $FileName); while (1) { my ($NextEntryOffset, $Action, $FileNameLength) = unpack("LLL" +, $pBuffer); (undef, undef, undef, $FileName) = unpack("LLLa$FileNameLength +", $pBuffer); $FileName = pack "C*", unpack "S*", $FileName; push @results, $Action => $FileName; last if($NextEntryOffset <= 0); $pBuffer = substr($pBuffer, $NextEntryOffset); } } #print Win32::GetLastError();
I get access denied as it is. If I use Win32API::File::DeviceIoControl instead, I get some error about the handle.

Im not even sure this is enough, and if it will work. I mean, Im not sure I can send a file to the DeviceIoControl, and that returns all sectors the file uses, or I somehow need to use FSCTL_GET_RETRIEVAL_POINTERS too. ( http://www.wd-3.com/archive/luserland.htm )
I get the same error with FSCTL_GET_RETRIEVAL_POINTERS (used in DeviceIOControl as second parameter) too though, as I tried to get all clusters a file is on, just like they do here: http://www.sysinternals.com/Information/DiskDefragmenting.html , but with no luck...

So, what am I missing here? And, how am I supposed to know the size of for instance lpInBuffer? ( sizeof(lpInBuffer) ) doesn't really work in Perl)...

Oh, and btw, you may have to change the path to the file.


UPDATE: Updated the code with the privileges enableing... No luck... But, after some research, I think I dont need the IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, but I have to use FSCTL_GET_RETRIEVAL_POINTERS...

Thanks,
Ace

Replies are listed 'Best First'.
Re: From File to at Sectors on harddrive (Windows)
by dfaure (Chaplain) on Mar 21, 2006 at 15:44 UTC

    Using the FILE_FLAG_BACKUP_SEMANTICS flag when calling CreateFile(...) requires some extra privileges to be enabled before proceding (cf. MSDN).

    This was in fact the purpose of the _EnablePrivileges() and related subroutines you may have noticed in Win32::ReadDirectoryChangesW.

    ____
    HTH, Dominique
    My two favorites:
    If the only tool you have is a hammer, you will see every problem as a nail. --Abraham Maslow
    Bien faire, et le faire savoir...

      Updated with previleges. No progress.

        I experiment a little with the same idea, and I have something that might work :

        #!perl use Win32API::File qw(:Func :FILE_ :FILE_SHARE_); sub FILE_FLAG_BACKUP_SEMANTICS () { 0x0200_0000 } sub FSCTL_GET_RETRIEVAL_POINTERS () { 0x90073 } my $f = $0; $f =~ s,/,\\,go; # CreateFile->Call # FH, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANT +ICS, NULL; my $fh= createFile( "$f", { Access => 'r', Share => FILE_SHARE_READ, Create => 'e', #Attributes=>"hst", Flags=>FILE_FLAG_BACKUP_SEMANTICS() } ) or die "Can't create file, $0: $^E\n"; my $vcn = 0 ; # virtual cluster number ... my $inBuf = pack ('L2',$vcn,0); # LARGE: Quad hence L2 !!! ; my $outBuf = pack ('L8',0); # ExCount,StartVCN,NextVCN,LCN my $dwByteReturned = 0; # DWORD; printf "Control code: %x\n",FSCTL_GET_RETRIEVAL_POINTERS; DeviceIoControl($fh, FSCTL_GET_RETRIEVAL_POINTERS, $inBuf,length($inBuf), $outBuf,length($outBuf), $dwBytesReturned, []); my $error = Win32::GetLastError(); print "error-no: $error\n"; warn "DeviceIoControl->Call: ", Win32::FormatMessage($error) if ($er +ror); printf "BytesReturned: %u\n",$dwBytesReturned; printf "outBuf %s\n",unpack 'H*',substr($outBuf,0,80); 1;

        Let me know if it works for you ?
        +Michel