Mouri_Naruto 发表于 2016-8-16 19:41

【技术·水】浅谈Windows10的CompactOS特性

本帖最后由 Mouri_Naruto 于 2016-8-16 19:56 编辑

================================注释开始================================
转载前最好告知楼主一声

感谢不离不弃的包子,zifeityzyicq和ART-Master

本技术贴原名《【水·技术】浅谈Windows 10 Build 9879的磁盘清理的System Compression》
远景地址:http://bbs.pcbeta.com/viewthread-1567726-1-1.html
================================注释结束================================

一:前言

上次,我在远景发布了《【水·技术】浅谈Windows 10 Build 9879的磁盘清理的System Compression》;
虽然文笔稚嫩(可以算黑历史了),但依旧上了远景首页

这个帖子发布了快两年了,这个世界也有了不少变化

Windows10早已从Build 9879进入到了Build 14901;
System Compression也改名为CompactOS;
Windows10下可以使用compact命令行工具;Win7/8/8.1也有Dism++可以帮你;
MSDN也公开了CompactOS的调用方法

在这样的情形下,我打算写个总结帖子

二:CompactOS原理

CompactOS是通过Windows Overlay Filter过滤驱动实现的,该过滤驱动同时也负责WIMBoot的实现

CompactOS在一般人看来如同NTFS压缩和Bitlocker加密一样是透明的
但是CompactOS可以选用压缩率更高的用于压缩WIM文件的算法,你可以使用以下算法

XPRESS4K(WIMBoot压缩算法,CompactOS默认算法;压缩率最低,速度最快)
XPRESS8K
XPRESS16K
LZX(WIM最大压缩;压缩率最高,速度最慢)

于是这也是CompactOS比只使用LZNT压缩算法的NTFS压缩压缩率更高的奥秘所在

三:如何使用CompactOS压缩

第一种途径,你可以通过Windows10开始系统自带的compact命令行工具对文件使用CompactOS压缩

这个用法只适用于Windows 10 Build 10074或更高版本

同时按下键盘上的Win和X键,出现菜单;点击命令提示符(管理员);
接下来弹出用户账户控制对话框,点是就出现了命令提示符窗口。

若安装系统时想对系统文件使用默认CompactOS算法进行全盘压缩,可以执行这样的指令:
DISM /Apply-Image /ImageFile:install.wim /Index:1/ApplyDir:D:\ /compact (D为系统盘符)

如果你要查询你的系统有没有被CompactOS,可以在管理员权限的命令提示符下输入以下命令并回车(在线系统适用)
compact /compactos:query
如果你的系统被CompactOS的话,会显示以下信息:“系统处于压缩状态。它将保持此状态,除非管理员更改它。”

如果你想要进行OSCompact的话,可以在管理员权限的命令提示符下输入以下命令并回车(在线系统适用)
compact /compactos:always

如果你要撤销OSCompact,可以在管理员权限的命令提示符下输入以下命令并回车(在线系统适用)
compact /compactos:never

如果你想单独对一个文件进行CompactOS压缩,那怎么办呢?下面举个例子

如果我要对E:\ToolKit\procexp.exe进行lzx算法的Compact压缩,我可以输入以下命令并回车(在线和离线都可以)。
compact /c /exe:lzx E:\ToolKit\procexp.exe

既然介绍了压缩,那么介绍如何解压也是不可少的。
compact /u /exe E:\ToolKit\procexp.exe

如果你的系统不符合要求,或者不习惯命令提示符操作,推荐使用Dism++。使用方法参考Dism++的相关说明,这里不再叙述。

第二种途径,通过API调用对文件进行CompactOS压缩

你可以通过DeviceIoControl传入相应IO控制码实现对文件的CompactOS压缩

SDK要求Windows 10 Build 10240及之后版本;且使用CompactOS前要
#include <winioctl.h>

而且CompactOS压缩对传入和传出的结构有要求
WOF_EXTERNAL_INFO和FILE_PROVIDER_EXTERNAL_INFO_V1这两个结构的内容要放在一个Buffer内传输
且WOF_EXTERNAL_INFO结构的内容要放在FILE_PROVIDER_EXTERNAL_INFO_V1结构内容的前面

为了方便起见,我定义了一个结构
typedef struct _WOF_FILE_EXTERNAL_BACKING_V1
{
      WOF_EXTERNAL_INFO WofInfo;
      FILE_PROVIDER_EXTERNAL_INFO_V1 FileProviderInfo;
} WOF_FILE_EXTERNAL_BACKING_V1, *PWOF_FILE_EXTERNAL_BACKING_V1;

FSCTL_SET_EXTERNAL_BACKING - 压缩
FSCTL_GET_EXTERNAL_BACKING - 获取压缩信息
FSCTL_DELETE_EXTERNAL_BACKING - 解压缩

PS:CompactOS压缩过的文件,如果以可写方式打开系统会自动解压缩文件(也许微软这么做是为了保证性能)

为了方便起见,我用NativeAPI对CompactOS的调用做了封装

CompactLib.h
// CompactLib By Mouri_Naruto

#pragma once

// Wof压缩算法定义

#define FILE_PROVIDER_COMPRESSION_XPRESS4K   (0x00000000)
#define FILE_PROVIDER_COMPRESSION_LZX      (0x00000001)
#define FILE_PROVIDER_COMPRESSION_XPRESS8K   (0x00000002)
#define FILE_PROVIDER_COMPRESSION_XPRESS16K(0x00000003)
#define FILE_PROVIDER_COMPRESSION_MAXIMUM    (0x00000004)

/*
对指定文件执行Wof压缩
*/
HRESULT WINAPI WofCompactFile(
        _In_ HANDLE FileHandle,
        _In_ DWORD CompactAlgorithm
        );

/*
对指定文件执行Wof解压缩
*/
HRESULT WINAPI WofUnCompactFile(
        _In_ HANDLE FileHandle
        );

/*
获取指定文件的Wof压缩算法
(如果文件未压缩或者执行失败返回 -1,否则为Wof压缩算法定义值)
*/
DWORD WINAPI WofGetFileCompactAlgorithm(
        _In_ HANDLE FileHandle
        );

/*
对指定文件执行NTFS压缩
*/
HRESULT WINAPI NTFSCompactFile(
        _In_ HANDLE FileHandle
        );

/*
对指定文件执行NTFS解压缩
*/
HRESULT WINAPI NTFSUnCompactFile(
        _In_ HANDLE FileHandle
        );

CompactLib.cpp

// CompactLib By Mouri_Naruto

#include <Windows.h>

#include "CompactLib.h"

#ifndef NativeAPI
#define NativeAPI

#pragma comment(lib,"ntdll.lib")

#define NT_SUCCESS(status) ((NTSTATUS)(status) >= 0)

typedef struct _IO_STATUS_BLOCK
{
        union
        {
                NTSTATUS Status;
                PVOID Pointer;
        };
        ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef VOID(NTAPI *PIO_APC_ROUTINE)(
        _In_ PVOID ApcContext,
        _In_ PIO_STATUS_BLOCK IoStatusBlock,
        _In_ ULONG Reserved
        );

extern "C"
{
        NTSYSCALLAPI NTSTATUS NTAPI NtFsControlFile(
                _In_ HANDLE FileHandle,
                _In_opt_ HANDLE Event,
                _In_opt_ PIO_APC_ROUTINE ApcRoutine,
                _In_opt_ PVOID ApcContext,
                _Out_ PIO_STATUS_BLOCK IoStatusBlock,
                _In_ ULONG FsControlCode,
                _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer,
                _In_ ULONG InputBufferLength,
                _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer,
                _In_ ULONG OutputBufferLength
                );

        NTSYSAPI ULONG NTAPI RtlNtStatusToDosError(
                _In_ NTSTATUS Status
                );
}
#endif

#include <winioctl.h>

typedef struct _WOF_FILE_EXTERNAL_BACKING_V1
{
        WOF_EXTERNAL_INFO WofInfo;
        FILE_PROVIDER_EXTERNAL_INFO_V1 FileProviderInfo;
} WOF_FILE_EXTERNAL_BACKING_V1, *PWOF_FILE_EXTERNAL_BACKING_V1;

HRESULT WINAPI WofCompactFile(
        _In_ HANDLE FileHandle,
        _In_ DWORD CompactAlgorithm
        )
{
        if (WofGetFileCompactAlgorithm(FileHandle) == CompactAlgorithm)
                return S_OK;
       
        NTSTATUS status;
        IO_STATUS_BLOCK IoStatus = { 0 };
        WOF_FILE_EXTERNAL_BACKING_V1 WofFileInfo = { 0 };

        WofFileInfo.WofInfo.Version = WOF_CURRENT_VERSION;
        WofFileInfo.WofInfo.Provider = WOF_PROVIDER_FILE;
        WofFileInfo.FileProviderInfo.Version = FILE_PROVIDER_CURRENT_VERSION;
        WofFileInfo.FileProviderInfo.Algorithm = CompactAlgorithm;
        WofFileInfo.FileProviderInfo.Flags = 0;

        status = NtFsControlFile(
                FileHandle, NULL, NULL, NULL, &IoStatus, FSCTL_SET_EXTERNAL_BACKING,
                &WofFileInfo, sizeof(WofFileInfo), NULL, 0);

        return RtlNtStatusToDosError(status);
}

HRESULT WINAPI WofUnCompactFile(
        _In_ HANDLE FileHandle
        )
{
        NTSTATUS status;
        IO_STATUS_BLOCK IoStatus = { 0 };

        status = NtFsControlFile(
                FileHandle, NULL, NULL, NULL, &IoStatus,
                FSCTL_DELETE_EXTERNAL_BACKING, NULL, 0, NULL, 0);

        return RtlNtStatusToDosError(status);
}

DWORD WINAPI WofGetFileCompactAlgorithm(
        _In_ HANDLE FileHandle
        )
{
        NTSTATUS status;
        IO_STATUS_BLOCK IoStatus = { 0 };
        WOF_FILE_EXTERNAL_BACKING_V1 WofFileInfo = { 0 };

        status = NtFsControlFile(
                FileHandle, NULL, NULL, NULL, &IoStatus, FSCTL_GET_EXTERNAL_BACKING,
                NULL, 0, &WofFileInfo, sizeof(WofFileInfo));

        return !NT_SUCCESS(status) ? -1 : WofFileInfo.FileProviderInfo.Algorithm;
}

HRESULT WINAPI NTFSCompactFile(
        _In_ HANDLE FileHandle
        )
{
        NTSTATUS status;
        IO_STATUS_BLOCK IoStatus = { 0 };
        USHORT Type = COMPRESSION_FORMAT_DEFAULT;

        status = NtFsControlFile(
                FileHandle, NULL, NULL, NULL, &IoStatus,
                FSCTL_SET_COMPRESSION, &Type, sizeof(Type), NULL, 0);

        return RtlNtStatusToDosError(status);
}

HRESULT WINAPI NTFSUnCompactFile(
        _In_ HANDLE FileHandle
        )
{
        NTSTATUS status;
        IO_STATUS_BLOCK IoStatus = { 0 };
        USHORT Type = COMPRESSION_FORMAT_NONE;

        status = NtFsControlFile(
                FileHandle, NULL, NULL, NULL, &IoStatus,
                FSCTL_SET_COMPRESSION, &Type, sizeof(Type), NULL, 0);

        return RtlNtStatusToDosError(status);
}

四:性能影响和效果

度娘看了看,几乎都表示CompactOS基本上不会影响性能。但CompactOS易产生难整理型文件碎片,在机械硬盘上应注意。

下面附带下个人的体验
由于我使用了LZX算法,于是系统盘比以前腾出了将近一半的空间(原本占用50GB,现在25GB左右)
而且感受不到性能的损失,也许是我的CPU是E3 1230 V2,硬盘是256GB SSD的缘故吧

五:移植CompactOS特性到其他系统

前提条件系统是Windows 7/8/8.1及对应的服务器版本;而且你要使用Windows ADK 10的wofadk.sys

曾经和Dism++作者解决Dism++在Windows Vista下的兼容性问题时
发现Vista下一旦安装了wofadk驱动,bootmgr阶段会报错“启动文件证书无效”

而且你要排除Windows加载wofadk驱动前Windows需要用到的文件

详情可以参考无忧;或者你也可以使用Dism++

附:Windows10 CompactOS排除列表
(IDA打开Windows10的compact.exe命令行工具就可以找到了)
// 文件排除列表
wchar_t *FileExclusionList[] =
{
      L"\\aow.wim",
      L"\\boot\\bcd",
      L"\\boot\\bcd.log",
      L"\\boot\\bootstat.dat",
      L"\\config\\drivers",
      L"\\config\\drivers.log",
      L"\\config\\system",
      L"\\config\\system.log",
      L"\\windows\\bootstat.dat",
      L"\\winload.efi",
      L"\\winload.efi.mui",
      L"\\winload.exe",
      L"\\winload.exe.mui",
      L"\\winresume.efi",
      L"\\winresume.efi.mui",
      L"\\winresume.exe",
      L"\\winresume.exe.mui"
};

// 目录排除列表
wchar_t *DirectoryExclusionList[] =
{
      L"\\Backup\\",
      L"\\ManifestCache\\",
      L"\\Manifests\\"
};

感谢阅读,毛利于2016/8/16



Purewhite 发表于 2016-8-16 22:29

不明觉厉啊。。

dapinggue 发表于 2016-8-16 23:11

我也是这么觉得

lolxzz 发表于 2016-8-18 06:38

C++ 好久没看了

os51 发表于 2016-8-18 10:13

http://www.52pojie.cn/forum-24-1.html
建议放到这里

Mouri_Naruto 发表于 2016-8-18 12:33

os51 发表于 2016-8-18 10:13
http://www.52pojie.cn/forum-24-1.html
建议放到这里

建议got,谢谢

gcczm 发表于 2016-8-18 17:23


不明觉厉啊。。

旧城迷梦 发表于 2016-8-18 19:08

不明觉厉加1。。。

chenmintian 发表于 2017-5-14 23:00

搜索到了.
谢楼主.

anyta 发表于 2017-12-2 12:44


不明觉厉啊。。
页: [1] 2
查看完整版本: 【技术·水】浅谈Windows10的CompactOS特性