Jump to content

WOF File Set Compression?


Recommended Posts

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.

Link to comment
Share on other sites

...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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

#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.

Link to comment
Share on other sites

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
 Share

  • Recently Browsing   0 members

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