Enable NTFS Compress on WinSxS Folder on Windows 10

Discussion in 'Windows 10' started by cyberthug, May 20, 2023.

  1. cyberthug

    cyberthug MDL Junior Member

    Jan 9, 2017
    62
    63
    0
    Hello, everything is in the title, what do you think of this manipulation, I am trying to reduce the winxs directory


    1. Firstly, open the command prompt as administrator.​
    2. Then, stop and disable Windows Installer and Windows Module Installer services:
    sc stop msiserver
    sc stop TrustedInstaller
    sc config msiserver start= disabled
    sc config TrustedInstaller start= disabled

    3. Let us backup the Access Control Lists (ACLs) assigned to the files and folders in the WinSxS directory using the built-in icacls tool. A backup copy of an ACL is a plain text file that lists all files and directories and the NTFS permissions assigned to them (later this file will be needed to restore the original ACLs):

    icacls “%WINDIR%\WinSxS” /save “%WINDIR%\WinSxS_NTFS.acl” /t

    4. Assign us as an owner of the WinSxS folder and all its subfolders:

    takeown /f “%WINDIR%\WinSxS” /r

    5. Grant our account full control permissions on the WinSxS directory:
    Code:
    icacls “%WINDIR%\WinSxS” /grant “%USERDOMAIN%\%USERNAME%”:(F) /t
    6. We can now compress files in the WinSxS directory using the compact command. Because some of the files can be used by Windows, we need to specify the /i option. Otherwise, compression will stop at the first locked file (in Windows 10, we can use more advanced LZX compression):

    compact /s:”%WINDIR%\WinSxS” /c /a /i *

    7. Restore the owner of the WinSxS directory back to TrustedInstaller:

    icacls “%WINDIR%\WinSxS” /setowner “NT SERVICE\TrustedInstaller” /t

    8. Restore the original ACLs for the WinSxS folder items using the ACL backup file we created earlier:

    icacls “%WINDIR%” /restore “%WINDIR%\WinSxS_NTFS.acl”

    9. Restore the default startup type for the Windows Installer and Windows Module Installer services:

    sc config msiserver start= demand
    sc config TrustedInstaller start= demand

    Now check the current size of the WinSxS folder.


     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  2. Dark Dinosaur

    Dark Dinosaur X Æ A-12

    Feb 2, 2011
    3,760
    5,223
    120
    Or .. use nsudo .. :)
    If you can't beat them join them
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  3. 78372

    78372 MDL Novice

    Mar 30, 2022
    36
    9
    0
    Does this work on other versions of windows (like windows 8.1) ?
     
  4. Dark Dinosaur

    Dark Dinosaur X Æ A-12

    Feb 2, 2011
    3,760
    5,223
    120
    #4 Dark Dinosaur, May 20, 2023
    Last edited: May 20, 2023
    with aveyo run as ti method.

    Code:
    @cls
    @echo off
    >nul chcp 437
    Title Compact OS Tool
    setlocal enabledelayedexpansion
    
    cd /d %~dp0
    set "fp=%~f0"
    
    :::: check if we running under system Rights
    whoami|>nul findstr /i /c:"nt authority\system" && (
      call :Run_AS_TI %*
      echo. & pause & exit /b
    )
    
    
    shift & set "args=%*"
    call :RunAsTI cmd /c call "%fp%" %args%
    exit
    
    :Run_AS_TI
    cls
    echo:
    
    set folder_list=^
    ,"%windir%\winsxs"^
    ,"%windir%\system32"^
    ,"%windir%\SysWOW64"^
    ,"%ProgramFiles%\WindowsApps"
    
    for %%$ in (%folder_list:;=,%) do (
      echo Compress Folder: %%$
      >nul compact /c /s:%%$ /exe:LZX /q /i
    )
    echo:
    goto :eof
    
    #:RunAsTI: #1 snippet to run as TI/System, with /high priority, /priv ownership, explorer and HKCU load
    set ^ #=& set "0=%~f0"& set 1=%*& powershell -nop -c iex(([io.file]::ReadAllText($env:0)-split':RunAsTI\:.*')[1])& exit/b
    $_CAN_PASTE_DIRECTLY_IN_POWERSHELL='^,^'; function RunAsTI ($cmd) { $id='RunAsTI'; $sid=((whoami /user)-split' ')[-1]; $code=@'
    $ti=(whoami /groups)-like"*1-16-16384*"; $DM=[AppDomain]::CurrentDomain."DefineDynamicAss`embly"(1,1)."DefineDynamicMod`ule"(1)
    $D=@(); 0..5|% {$D+=$DM."DefineT`ype"("M$_",1179913,[ValueType])}; $I=[int32];$P=$I.module.gettype("System.Int`Ptr"); $U=[uintptr]
    $D+=$U; 4..6|% {$D+=$D[$_]."MakeB`yRefType"()};$M=$I.module.gettype("System.Runtime.Interop`Services.Mar`shal");$Z=[uintptr]::size
    $S=[string]; $F="kernel","advapi","advapi",($S,$S,$I,$I,$I,$I,$I,$S,$D[7],$D[8]),($U,$S,$I,$I,$D[9]),($U,$S,$I,$I,[byte[]],$I)
    0..2|% {$9=$D[0]."DefinePInvokeMeth`od"(("CreateProcess","RegOpenKeyEx","RegSetValueEx")[$_],$F[$_]+'32',8214,1,$S,$F[$_+3],1,4)}
    $DF=0,($P,$I,$P),($I,$I,$I,$I,$P,$D[1]),($I,$S,$S,$S,$I,$I,$I,$I,$I,$I,$I,$I,[int16],[int16],$P,$P,$P,$P),($D[3],$P),($P,$P,$I,$I)
    1..5|% {$k=$_;$n=1;$AveYo=1; $DF[$_]|% {$9=$D[$k]."DefineFie`ld"('f'+$n++,$_,6)}}; $T=@(); 0..5|% {$T+=$D[$_]."CreateT`ype"()}
    0..5|% {nv "A$_" ([Activator]::CreateInstance($T[$_])) -force}; function F ($1,$2) {$T[0]."GetMeth`od"($1).invoke(0,$2)};
    if (!$ti) { $g=0; "TrustedInstaller","lsass"|% {if (!$g) {net1 start $_ 2>&1 >$null; $g=@(get-process -name $_ -ea 0|% {$_})[0]}}
     function M($1,$2,$3){$M."GetMeth`od"($1,[type[]]$2).invoke(0,$3)}; $H=@(); $Z,(4*$Z+16)|% {$H+=M "AllocHG`lobal" $I $_};
     M "WriteInt`Ptr" ($P,$P) ($H[0],$g.Handle); $A1.f1=131072;$A1.f2=$Z;$A1.f3=$H[0];$A2.f1=1;$A2.f2=1;$A2.f3=1;$A2.f4=1;$A2.f6=$A1
     $A3.f1=10*$Z+32;$A4.f1=$A3;$A4.f2=$H[1]; M "StructureTo`Ptr" ($D[2],$P,[boolean]) (($A2 -as $D[2]),$A4.f2,$false); $w=0x0E080600
     $out=@($null,"powershell -win 1 -nop -c iex `$env:A",0,0,0,$w,0,$null,($A4 -as $T[4]),($A5 -as $T[5])); F "CreateProcess" $out
    } else { $env:A=''; $PRIV=[uri].module.gettype("System.Diagnostics.Process")."GetMeth`ods"(42) |? {$_.Name -eq "SetPrivilege"}
     "SeSecurityPrivilege","SeTakeOwnershipPrivilege","SeBackupPrivilege","SeRestorePrivilege" |% {$PRIV.Invoke(0, @("$_",2))}
     $HKU=[uintptr][uint32]2147483651; $LNK=$HKU; $reg=@($HKU,"S-1-5-18",8,2,($LNK -as $D[9])); F "RegOpenKeyEx" $reg; $LNK=$reg[4]
     function SYM($1,$2){$b=[Text.Encoding]::Unicode.GetBytes("\Registry\User\$1");@($2,"SymbolicLinkValue",0,6,[byte[]]$b,$b.Length)}
     F "RegSetValueEx" (SYM $(($key-split'\\')[1]) $LNK); $EXP="HKLM:\Software\Classes\AppID\{CDCBCFCA-3CDC-436f-A4E2-0E02075250C2}"
     $r="explorer"; if (!$cmd) {$cmd='C:\'}; $dir=test-path -lit ((($cmd -split '^("[^"]+")|^([^\s]+)') -ne'')[0].trim('"')) -type 1
     if (!$dir) {$r="start `"$id`" /high /w"}; sp $EXP RunAs '' -force; start cmd -args ("/q/x/d/r title $id && $r",$cmd) -wait -win 1
     do {sleep 7} while ((gwmi win32_process -filter 'name="explorer.exe"'|? {$_.getownersid().sid -eq "S-1-5-18"}))
     F "RegSetValueEx" (SYM ".Default" $LNK); sp $EXP RunAs "Interactive User" -force } # lean and mean snippet by AveYo, 2018-2021
    '@; $key="Registry::HKEY_USERS\$sid\Volatile Environment"; $a1="`$id='$id';`$key='$key';";$a2="`$cmd='$($cmd-replace"'","''")';`n"
    sp $key $id $($a1,$a2,$code) -type 7 -force; $arg="$a1 `$env:A=(gi `$key).getvalue(`$id)-join'';rp `$key `$id -force; iex `$env:A"
    $_PRESS_ENTER='^,^'; start powershell -args "-win 1 -nop -c $arg" -verb runas }; <#,#>  RunAsTI $env:1;  #:RunAsTI:
    
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  5. acer-5100

    acer-5100 MDL Guru

    Dec 8, 2018
    3,772
    2,696
    120
    #5 acer-5100, May 20, 2023
    Last edited: May 20, 2023

    That's really overkill.

    And also, it compress using only the LZX compression, which doesn't inherit the compressed status


    A way better approach is.

    #1 First compress your whole OS partition using the good old NTFS compression

    Just launch a 3d party file manager like xy explorer, launch it as trusted installer and compress the whole partition (preferably starting from a parallel OS or WinPE bootable media)

    #2 Capture the partition using wimlib-capture

    #3 Format the partition and restore the wim using wimlib-apply using the switch -compact=LZX

    #4 Given the process breaks the bcd entries, use bcdboot or bootice or easybcd, to re-add your OS to bcd.


    This way all immutable files will be LZX compressed, while anything that changes will be still compressed although using the standard NTFS compression.

    No need to touch ACLs, no need to mess with permissions ownership and alike, and if you are on SSD the system will be also faster (if you are on HDD just avoid compression)
     
  6. hoak

    hoak MDL Member

    Nov 13, 2009
    143
    158
    10
    Is there a way to do this during OS installation?
     
  7. acer-5100

    acer-5100 MDL Guru

    Dec 8, 2018
    3,772
    2,696
    120
    #7 acer-5100, May 21, 2023
    Last edited: May 21, 2023
    Yes and no.

    I mean, OS "installation" is matter of the past.

    All you need is starting from #3, no matter if you need compression or not. Just learn to deploy whenever you need a fresh install. (I mean use the ISO and setup.exe just for in place upgrades)


    That said if you start from #3 and use a normal install media you will get your shiny fresh and small OS.

    But this way the OS will be compressed only using the LZX compression, which means very small initial footprint but the OS will grow fast because the LZX compression doesn't recompress the compressed files (open a compressed file, change a single bit on it, save it, and your file will be magically decompressed).

    What you can do is: take the install.wim, mount it and compress it using the standard NTFS compression (use the xy explorer + nsudo method described above)

    Then unmount the install.wim (committing the changes), and (if you want) replace it in the ISO.

    This way any installation/deployment made using it will be NTFS compressed (which is already a great thing, 1.6 x less space taken and a faster OS on SSD).

    But, if you deploy that install.wim using wimlib-apply with the --compact=LZX switch, the OS will be BOTH NTFS and LZX compressed.

    The initial compression ratio will be between 2.5 and 3.0x (aka a fresh x64 install will take less than 6GB instead of ~16GB, and a x86 install will take less than 4GB instead of ~10GB.

    Using the OS (installing new SW, cumulative updates and so on) that size obviously will grow, but will grow way less than a system which is compressed only with the modern LZX compression.

    P.S. before someone ask, no the two compression method doesn't step each other feet. Because the old NTFS compression was made purposely to not try to recompress an already compressed file
     
  8. acer-5100

    acer-5100 MDL Guru

    Dec 8, 2018
    3,772
    2,696
    120

    The compact command, while compresses on LZX (or Xpress-Something) removes the ntfs compression making the whole operation almost useless.

    Unless you repeat it each time you apply an LCU or each time you install a big program.

    The truth is that the modern compression methods are half backed, i guess MS did it for backward compatibility reasons, but as a matter of fact the 1993 or so NTFS compression is way more useful than the LZX compression (if used alone).
     
  9. LiteOS

    LiteOS Windowizer

    Mar 7, 2014
    2,209
    982
    90
    the ntfs compression is not compact ?:confused:
    i thought kernel needs this specific compression method to be able to read on-the-fly
     
  10. acer-5100

    acer-5100 MDL Guru

    Dec 8, 2018
    3,772
    2,696
    120
    #11 acer-5100, May 21, 2023
    Last edited: May 21, 2023
    Compact is the command

    the NTFS copmpression is a native filesystem feature which is there since Winnt 3.x.

    Given the CPU and the RAM of the time it was developed as a low taxing method.

    Which means today it requires practically zero effort by the CPU.

    The compact command was used to manage it, but it's also accessible from the GUI.

    upload_2023-5-21_22-53-37.png


    Since W8.1 the four new compression methods were introduced, but they are not integrated in the filesystem.

    They are a layer on top of it (like the deduplication).

    That method was then perfected in Win10.

    BUT

    There isn't any GUI to manage it, not there is any way to keep the compressed status after an edit, nor any way to inherit the compressed status ftom the parent folder or disk.

    the only tools that manage it are the compact command (which accepts more switches), and DISM (and its better open source cousin wimlib).

    AFAIK there isn't any way (other than my method) to keep both the NTFS compression flag AND the LZX (or Xpress) compression.

    But maybe I'm missing something, if anyone has a better method than mine, I'm all ears.


    P.S. the screenshot above is a full Server 2016 installation, no package removed and all the SW I use installed, Including VMware, VBox, WMC, various TV server SW, OFFICE Starter, and so on. It takes 8GB, half of what Server 2016 would normally requires W/O any additional SW installed.

    P.S.2 Althoug the method is fully working only on W10, it can be extended to W7/W8/W8.1 (x86 and AMD64) all you need is the wofadk.sys driver (winntsetup.exe can automate the procedure)
     
  11. Dark Dinosaur

    Dark Dinosaur X Æ A-12

    Feb 2, 2011
    3,760
    5,223
    120
    This command is the command-line version of the NTFS file system compression feature. :D

    Which by all means says .. it's a ntfs compression for specific folders
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  12. acer-5100

    acer-5100 MDL Guru

    Dec 8, 2018
    3,772
    2,696
    120

    [Pedantic mode=on]

    This command WAS the command meant to manage the NTFS file system compression feature

    now it manages both the NTFS compression feature and the "Compact OS" compression.

    And both are not by folder, but also by single file or by whole disk

    [Pedantic mode=off]

    :p

    To put it more colloquially the Compact OS feature is something which is half way between the NTFS native compression and something like a file compressor like .ZIP or .CAB.