Jump to content

Recommended Posts

Posted (edited)

So, I thought I'd take a crack at porting wimlib's compact functionality to AutoIt so that I could essentially Compact files like compact.exe does.
After parsing the various funcs, the process is as follows...
1. NtCreateFile to open a file and get hFile

2.  Create some structs with some flags for the compression type

3. call NtFsControlFile with $FSCTL_SET_EXTERNAL_BACKING command

Now, oddly enough everything returns with a success, yet the file remains uncompressed, even from Compact's point of view

Any ideas?
 

Chicken Scratch:

#RequireAdmin
#include <Array.au3>
#include <WinAPIDiag.au3>
#include <WinAPIFiles.au3>

Global Const $tagWOF_EXTERNAL_INFO="uint WOFEI_Version;uint WOFEI_Provider"
Global Const $tagFILE_PROVIDER_EXTERNAL_INFO="uint FPEI_Version;uint FPEI_CompressionFormat"
Global Const $tagIOSTATUSBLOCK = "ptr Status;ptr Information"
Global Const $tagOBJECTATTRIBUTES = "ulong Length;handle RootDirectory;ptr ObjectName;ulong Attributes;ptr SecurityDescriptor;ptr SecurityQualityOfService"
Global Const $tagUNICODESTRING = "ushort Length;ushort MaximumLength;ptr Buffer"
Global Const $FILE_OPEN=0x00000001
Global Const $STATUS_PENDING=0x00000103
Global Const $STATUS_SUCCESS=0x00000000
Global Const $STATUS_ACCESS_DENIED=0xC0000022
Global Const $OBJ_CASE_INSENSITIVE = 0x00000040
Global Const $FILE_SHARE_VALID_FLAGS=0x00000007
Global Const $FILE_OPEN_REPARSE_POINT=0x00200000
Global Const $FSCTL_SET_EXTERNAL_BACKING=0x9030C
Global Const $FILE_OPEN_FOR_BACKUP_INTENT=0x00004000
Global Const $STATUS_INVALID_DEVICE_REQUEST=0xC0000010
Global Const $STATUS_COMPRESSION_NOT_BENEFICIAL=0xC000046F
Global Const $FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K=0
Global Const $FILE_PROVIDER_COMPRESSION_FORMAT_LZX=1
Global Const $FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K=2
Global Const $FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K=3
Global Const $FILE_PROVIDER_CURRENT_VERSION=1
Global Const $WOF_CURRENT_VERSION=1
Global Const $WOF_PROVIDER_FILE=2

Global $hDll_NTDLL=DllOpen("ntdll.dll")
MsgBox(64,"",FileGetSize("C:\Test.dat")&@CRLF)
ConsoleWrite(_WinAPI_SetSystemCompression("C:\Test.dat",$FILE_PROVIDER_COMPRESSION_FORMAT_LZX)&"|"&@Error&"|"&@Extended&@CRLF)
MsgBox(64,"",FileGetSize("C:\Test.dat")&@CRLF)

; Try to attach an instance of the Windows Overlay Filesystem filter driver
; to the specified drive (such as C:)
Func DriveAttachWOF($sDrive); win32_try_to_attach_wof
    Local $iRet,$hDll_FltLib
    $hDll_FltLib=DllOpen("FltLib.dll")
    If @error Then Return SetError(1,0,0)
    $aRet=DllCall($hDll_FltLib,"int","FilterAttach","WSTR","wof","wstr",$sDrive,"ptr",0,"int",0,"ptr",0)
    If @error Or Not $aRet[0] Then
        $aRet=DllCall($hDll_FltLib,"int","FilterAttach","WSTR","wofadk","wstr",$sDrive,"ptr",0,"int",0,"ptr",0)
        If @error Or Not $aRet[0] Then Return SetError(@error, @extended, 0)
    EndIf
    DllClose($hDll_FltLib)
    Return SetError(0,0,$aRet[0])
EndFunc

Func PathGetDrive($sPath)
    $sPath=_WinAPI_GetFullPathName($sPath)
    If @error Or $sPath="" Then Return SetError(1,0,0)
    $aTest=StringRegExp($sPath,"^([A-Za-z]\:).*$",1)
    If @error Then Return SetError(1,0,0)
    Return SetError(0,0,"\\.\"&StringLower($aTest[0]))
EndFunc

Func _WinAPI_SetSystemCompression($sFilePath,$iFormat=0)
    Local $isWin10=0,$iCompatFlag=0
    If @OSVersion="WIN_10" Then $isWin10=1
        ;$sFilePath="\\.\"&_WinAPI_GetFullPathName($sFilePath) (TODO)

    ;For compatibility with the Windows bootloader (TODO)
    ;incompatible = match_pattern_list(dentry->d_full_path,&bootloader_patterns);
    If $iCompatFlag And ($isWin10 Or $iFormat<>0) Then
        If $isWin10 Then;Win10 only supports XPRESS4K format
            $iFormat=0
        Else;Anything Else cannot handle boot file compression, die
            Return SetError(1,1,0)
        EndIf
    EndIf
    $tName=DllStructCreate("wchar[260]")
    $tUName= DllStructCreate($tagUNICODESTRING)
    $tOA=DllStructCreate($tagOBJECTATTRIBUTES)
    DllStructSetData($tName,1,$sFilePath)
    $aRet=DllCall($hDll_NTDLL,"none","RtlInitUnicodeString","ptr",DllStructGetPtr($tUName),"ptr",DllStructGetPtr($tName))
    If @error Or Not $aRet[0] Then Return SetError(@Error,@Extended,0)
    ;MsgBox(64,"RtlInitUnicodeString",_WinAPI_GetLastErrorMessage())
    DllStructSetData($tOA,"Length",DllStructGetSize($tOA))
    DllStructSetData($tOA,"RootDirectory",0)
    DllStructSetData($tOA,"ObjectName",DllStructGetPtr($tUName))
    DllStructSetData($tOA,"Attributes",$OBJ_CASE_INSENSITIVE)
    DllStructSetData($tOA,"SecurityDescriptor",0)
    DllStructSetData($tOA,"SecurityQualityOfService",0)
    $tIOSB=DllStructCreate($tagIOSTATUSBLOCK)
    $tFile=DllStructCreate("HWND")
    $pFile=DllStructGetPtr($tFile)
    $aRet=DllCall($hDll_NTDLL,"handle","NtCreateFile", _
                             "handle*",$pFile, _
                               "ulong",BitOR($GENERIC_READ,$GENERIC_WRITE,20), _
                                 "ptr",DllStructGetPtr($tOA), _
                                 "ptr",DllStructGetPtr($tIOSB), _
                                 "ptr",0, _
                               "ulong",0, _
                               "ulong",$FILE_SHARE_VALID_FLAGS, _
                               "ulong",$FILE_OPEN, _
                               "ulong",BitOR($FILE_OPEN_FOR_BACKUP_INTENT,$FILE_OPEN_REPARSE_POINT), _
                                 "ptr",0, _
                               "ulong",0)
    If @error Or Not $aRet[0] Then Return SetError(@Error,@Extended,0)
    ;MsgBox(64,"NtCreateFile",_WinAPI_GetLastErrorMessage())
    $tInputBuffer=DllStructCreate("STRUCT;"&$tagWOF_EXTERNAL_INFO&";ENDSTRUCT;STRUCT;"&$tagFILE_PROVIDER_EXTERNAL_INFO&";ENDSTRUCT")
    DllStructSetData($tInputBuffer,"WOFEI_Version",$WOF_CURRENT_VERSION)
    DllStructSetData($tInputBuffer,"WOFEI_Provider",$WOF_PROVIDER_FILE)
    DllStructSetData($tInputBuffer,"FPEI_Version",$FILE_PROVIDER_CURRENT_VERSION)
    DllStructSetData($tInputBuffer,"FPEI_CompressionFormat",$iFormat)
    $pInputBuffer=DllStructGetPtr($tInputBuffer)
    $iInputBuffer=DllStructGetSize($tInputBuffer)
    Local $iTried=0
    Do
        $iRet=_WinAPI_NtFsControlFile(DllStructGetData($tFile,1),$FSCTL_SET_EXTERNAL_BACKING,$pInputBuffer,$iInputBuffer)
        If @error Then Return SetError(@Error,@Extended,0)
        ;MsgBox(64,"_WinAPI_NtFsControlFile",_WinAPI_GetLastErrorMessage())
        If $iRet=$STATUS_INVALID_DEVICE_REQUEST And Not $iTried Then
            DriveAttachWOF(PathGetDrive($sFilePath))
            $iTried=1
            ContinueLoop
        EndIf
        $iTried=1
    Until $iTried

    ;_ArrayDisplay($aRet,@error)

    Return 0;SetError(0,0,$iRet[0])
EndFunc
;typedef uint32_t u32;

;~  /* We intentionally use NtFsControlFile() rather than DeviceIoControl()
;~   * here because the "compressing this object would not save space"
;~   * status code does not map to a valid Win32 error code on older
;~   * versions of Windows (before Windows 10?).  This can be a problem if
;~   * the WOFADK driver is being used rather than the regular WOF, since
;~   * WOFADK can be used on older versions of Windows.  */
;~  status = winnt_fsctl(h, $FSCTL_SET_EXTERNAL_BACKING,
;~               &in, sizeof(in), NULL, 0, NULL);

; Synchronously execute a filesystem control method.  This is a wrapper around
; NtFsControlFile() that handles STATUS_PENDING.  Note that SYNCHRONIZE
; permission is, in general, required on the handle.
Func _WinAPI_NtFsControlFile($hFile,$iFsControlCode,$pInputBuffer,$iInputBuffer,$pOutputBuffer=0,$iOutputAvail=0)
    $tIOSB=DllStructCreate($tagIOSTATUSBLOCK)
    $pIOSB=DllStructGetPtr($tIOSB)
    $aRet=DllCall($hDll_NTDLL,"int","NtFsControlFile", _
    "HANDLE",$hFile, _
    "ptr",0, _
    "ptr",0, _
    "ptr",0, _
    "ptr",$pIOSB, _
    "uint",$iFsControlCode, _
    "ptr",$pInputBuffer, _
    "uint",$iInputBuffer, _
    "ptr",$pOutputBuffer, _
    "uint",$iOutputAvail)
    If @Error Or Not $aRet[0] Then Return SetError(@Error,@Extended,0)
    ;~     MsgBox(64,"NtFsControlFile",_WinAPI_GetLastErrorMessage())

    If $aRet[0]=$STATUS_PENDING Then;Rare
        $iRet=_WinAPI_WaitForSingleObject($hFile)
        $iError=_WinAPI_GetLastError()
        If Not $iError Then SetError(1,$iError,0)
        Return SetError(1,$iRet,0)
    EndIf
    Return SetError(0,0,$aRet[0])
EndFunc

 

Edited by Biatu

What is what? What is what.

Posted (edited)

...that moment when I realized that I was reinventing the wheel :sweating:

_WinAPI_SetCompression()

Edit: Wait  FSCTL_SET_COMPRESSION<>$FSCTL_SET_EXTERNAL_BACKING

Edited by Biatu

What is what? What is what.

Posted (edited)

I figured that I'd make a tray application that would periodically compact the system with LZX compression, and display the progress with a toast gui.

 

 

besides that, imo...having an autoit solution for file system compression would deem useful for the entire community.

Edited by Biatu

What is what? What is what.

Posted
#cs ----------------------------------------------------------------------------

 AutoIt Version: 3.3.14.2
 Author:         BiatuAutMiahn[@Outlook.com]

 Script Function:
    Set filesystem compression using windows' WOF file backing.
    Requires Win10 in most cases.
    
    Thanks to Danyfirex for making it actually work!

#ce ----------------------------------------------------------------------------
#include <WinAPI.au3>
#include <WinAPIFiles.au3>

Global Const $sTagWOF_EXTERNAL_INFO = "ulong WOFEI_Version;ulong WOFEI_Provider"
Global Const $sTagFILE_PROVIDER_EXTERNAL_INFO_V1 = "ulong FPEI_Version;ulong FPEI_CompressionFormat;ulong Flags"
Global Const $sTagIOSTATUSBLOCK = "ptr Status;ptr Information"
Global Const $sTagOBJECTATTRIBUTES = "ulong Length;handle RootDirectory;ptr ObjectName;ulong Attributes;ptr SecurityDescriptor;ptr SecurityQualityOfService"
Global Const $sTagUNICODESTRING = "ushort Length;ushort MaximumLength;ptr Buffer"
Global Const $FILE_OPEN = 0x00000001
Global Const $STATUS_PENDING = 0x00000103
Global Const $STATUS_SUCCESS = 0x00000000
Global Const $STATUS_ACCESS_DENIED = 0xC0000022
Global Const $OBJ_CASE_INSENSITIVE = 0x00000040
Global Const $FILE_SHARE_VALID_FLAGS = 0x00000007
Global Const $FILE_OPEN_REPARSE_POINT = 0x00200000
Global Const $FSCTL_SET_EXTERNAL_BACKING = 0x9030C
Global Const $FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000
Global Const $STATUS_INVALID_DEVICE_REQUEST = 0xC0000010
Global Const $STATUS_COMPRESSION_NOT_BENEFICIAL = 0xC000046F
Global Const $FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K = 0
Global Const $FILE_PROVIDER_COMPRESSION_FORMAT_LZX = 1
Global Const $FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K = 2
Global Const $FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K = 3
Global Const $FILE_PROVIDER_CURRENT_VERSION = 1
Global Const $WOF_CURRENT_VERSION = 1
Global Const $WOF_PROVIDER_FILE = 2

Global $hDll_NTDLL = DllOpen("ntdll.dll")
FileCopy("D:\wimlib-1.12.0-BETA1\wimlib-imagex.exe","D:\test.exe",1)
Local $sFilePath = "\??\D:\test.exe"
MsgBox(64, "", _WinAPI_GetCompressedFileSize($sFilePath) & @CRLF)
ConsoleWrite(_WinAPI_WofSetCompression($sFilePath, $FILE_PROVIDER_COMPRESSION_FORMAT_LZX)
MsgBox(64, "", _WinAPI_GetCompressedFileSize($sFilePath) & @CRLF)
DllClose($hDll_NTDLL)

Func _WinAPI_WofSetCompression($sFilePath, $iFormat)
    ; Local Const $FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
    Local $tFilePath=DllStructCreate("wchar[260]")
    Local $tFilePathW=DllStructCreate($sTagUNICODESTRING)
    Local $tObjAttr=DllStructCreate($sTagOBJECTATTRIBUTES)
    DllStructSetData($tFilePath,1,$sFilePath)
    Local $aRet=DllCall($hDll_NTDLL,"none","RtlInitUnicodeString","ptr",DllStructGetPtr($tFilePathW),"ptr",DllStructGetPtr($tFilePath))
    DllStructSetData($tObjAttr,"Length",DllStructGetSize($tObjAttr))
    DllStructSetData($tObjAttr,"RootDirectory",0)
    DllStructSetData($tObjAttr,"ObjectName",DllStructGetPtr($tFilePathW))
    DllStructSetData($tObjAttr,"Attributes",$OBJ_CASE_INSENSITIVE)
    DllStructSetData($tObjAttr,"SecurityDescriptor",0)
    DllStructSetData($tObjAttr,"SecurityQualityOfService",0)
    Local $tIOSB=DllStructCreate($sTagIOSTATUSBLOCK)
    Local $thFile=DllStructCreate("handle")
    Local $phFile=DllStructGetPtr($thFile)
    $aRet=DllCall($hDll_NTDLL,"long","NtCreateFile","ptr",$phFile,"ulong",BitOR($GENERIC_READ,$GENERIC_WRITE,0x20),"ptr",DllStructGetPtr($tObjAttr),"ptr",DllStructGetPtr($tIOSB),"int64*",0,"ulong",0,"ulong",$FILE_SHARE_VALID_FLAGS,"ulong",$FILE_OPEN,"ulong",BitOR($FILE_OPEN_FOR_BACKUP_INTENT,$FILE_OPEN_REPARSE_POINT),"ptr",0,"ulong",0)
    If @error Or $aRet[0]<>$STATUS_SUCCESS Then Return SetError(@error, @extended, 0)
    Local $hFile=DllStructGetData($thFile,1)
    Local $tInputBuffer=DllStructCreate("STRUCT;"&$sTagWOF_EXTERNAL_INFO&";ENDSTRUCT;STRUCT;"&$sTagFILE_PROVIDER_EXTERNAL_INFO_V1&";ENDSTRUCT")
    DllStructSetData($tInputBuffer,"WOFEI_Version",$WOF_CURRENT_VERSION)
    DllStructSetData($tInputBuffer,"WOFEI_Provider",$WOF_PROVIDER_FILE)
    DllStructSetData($tInputBuffer,"FPEI_Version",$FILE_PROVIDER_CURRENT_VERSION)
    DllStructSetData($tInputBuffer,"FPEI_CompressionFormat",$iFormat)
    Local $pInputBuffer=DllStructGetPtr($tInputBuffer)
    Local $iInputBuffer=DllStructGetSize($tInputBuffer)
    Local $iTried=0, $iRet=0
    Do
        $iRet=_WinAPI_NtFsControlFile($hFile,$FSCTL_SET_EXTERNAL_BACKING,$pInputBuffer,$iInputBuffer)
        If @error Then Return SetError(@error, @extended, 0)
        If $iRet=$STATUS_INVALID_DEVICE_REQUEST And Not $iTried Then
            __DriveAttachWOF(__PathGetDrive($sFilePath))
            $iTried=1
            ContinueLoop
        EndIf
        $iTried=1
    Until $iTried
    DllCall($hDll_NTDLL,"long","NtClose","handle",$hFile);close Handle
EndFunc   ;==>_WinAPI_SetCompression

Func _WinAPI_NtFsControlFile($hFile,$iFsControlCode,$pInputBuffer,$iInputBuffer,$pOutputBuffer=0,$iOutputAvail=0)
    Local $tIOSB=DllStructCreate($sTagIOSTATUSBLOCK)
    Local $pIOSB=DllStructGetPtr($tIOSB)
    Local $aRet=DllCall($hDll_NTDLL,"int","NtFsControlFile","HANDLE",$hFile,"ptr",0,"ptr",0,"ptr",0,"ptr",$pIOSB,"uint",$iFsControlCode,"ptr",$pInputBuffer,"uint",$iInputBuffer,"ptr",$pOutputBuffer,"uint",$iOutputAvail)
    If @error Or Not $aRet[0] Then Return SetError(@error,@extended,0)
    Local $iRet=0, $iError=0
    If $aRet[0]=$STATUS_PENDING Then
        $iRet=_WinAPI_WaitForSingleObject($hFile)
        $iError=_WinAPI_GetLastError()
        If Not $iError Then SetError(1,$iError,0)
        Return SetError(1,$iRet,0)
    EndIf
    Return SetError(0,0,$aRet[0])
EndFunc

Func __DriveAttachWOF($sDrive) ; win32_try_to_attach_wof
    Local $aRet,$hDll_FltLib
    $hDll_FltLib=DllOpen("FltLib.dll")
    If @error Then Return SetError(1,0,0)
    $aRet=DllCall($hDll_FltLib,"int","FilterAttach","WSTR","wof","wstr",$sDrive,"ptr",0,"int",0,"ptr",0)
    If @error Or Not $aRet[0] Then
        $aRet=DllCall($hDll_FltLib,"int","FilterAttach","WSTR","wofadk","wstr",$sDrive,"ptr",0,"int",0,"ptr",0)
        If @error Or Not $aRet[0] Then Return SetError(@error,@extended,0)
    EndIf
    DllClose($hDll_FltLib)
    Return SetError(0,0,$aRet[0])
EndFunc   ;==>DriveAttachWOF

Func __PathGetDrive($sPath)
    $sPath=_WinAPI_GetFullPathName($sPath)
    If @error Or $sPath="" Then Return SetError(1,0,0)
    Local $aTest=StringRegExp($sPath,"^([A-Za-z]\:).*$",1)
    If @error Then Return SetError(1,0,0)
    Return SetError(0, 0,"\\.\" & StringLower($aTest[0]))
EndFunc   ;==>PathGetDrive

 

What is what? What is what.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...