본문 바로가기

카테고리 없음

Delta Update with SWUpdate

 

 

 

https://sbabic.github.io/swupdate/delta-update.html

SWUpdateZchunk를 이용해 delta update 기능을 지원합니다.

FOSS projects for delta encoding

Zchunk - compression format

https://sbabic.github.io/swupdate/delta-update.html#zchunk-compression-format
https://github.com/zchunk/zchunk

이 프로젝트의 목표는 새로운 압축 포맷을 만들어 새 파일과 이전 파일 간의 차이점을 다운로드하는 기능을 추가하는 것입니다. zchunk 파일에는 모든 chunk에 대한 metadata가 있는 헤더가 포함되어 있으며 헤더에 따라 어떤 chunk를 다운로드해야 하고 어떤 chunk를 재사용할 수 있는지 알 수 있습니다. zchunk에는 누락된 chunk를 스스로 다운로드하는 유틸리티가 있지만 artifact에서 다운로드해야 하는 부분을 찾는 데만 사용할 수 있으며 SWUpdate는 자체적으로 이 작업을 수행할 수 있습니다.

이 접근 방식의 큰 장점 중 하나는 metadata와 압축 chunk가 여전히 단일 파일에 바인딩되어 빌드 시스템에서 빌드하고 익숙한 방식으로 전달할 수 있다는 것입니다. 업데이터는 먼저 metadata, 즉 zchunk 파일의 헤더가 필요하며, 이를 처리하여 다운로드해야 할 chunk를 감지합니다. 각 chunk에는 고유한 hash가 있으며, 장치에서 이미 사용 가능한 chunk가 손상되지 않았는지 확인하기 위해 hash와 비교하여 확인합니다.

Zchunk는 여러 SHA 알고리즘을 지원합니다. SWUpdate와 호환되려면 zchunk가 sha256 hash를 생성하도록 해야 합니다.

 

What is zchunk

https://www.jdieter.net/posts/2018/05/31/what-is-zchunk/

new file의 header를 다운로드 받습니다.

 

old file의 header의 정보를 비교합니다.

A, C chunk가 동일하므로 복사합니다.

 

new file의 D chunk는 서버에서 받습니다.

old file과 new file의 chunk 조합을 통해 new file이 완성됩니다.

 

zchunk format

https://github.com/zchunk/zchunk/blob/main/zchunk_format.txt

A zchunk file contains two parts, the header and the body. The header consists of four parts:

  • The lead: Everything necessary to validate the header
  • The preface: Metadata about the zchunk file
  • The index: Details about each chunk
  • The signatures: Signatures used to sign the zchunk file

The lead:
+-+-+-+-+-+====================+==================+=================+
|   ID    | Checksum type (ci) | Header size (ci) | Header checksum |
+-+-+-+-+-+====================+==================+=================+


The preface:
+===============+============+========================+
| Data checksum | Flags (ci) | Compression type (ci ) |
+===============+============+========================+

+=================================+
| Optional element count (ci) [1] |
+=================================+

[+==============================+=====================================+
[| Optional element id (ci) [1] | Optional element data size (ci) [1] |
[+==============================+=====================================+

+===========================+]
| Optional element data [1] |] ...
+===========================+]


The index:
+=================+==========================+==================+
| Index size (ci) | Chunk checksum type (ci) | Chunk count (ci) |
+=================+==========================+==================+

(Dict stream will only exist if flag 0 is set to 1)
+======================+===============+================================+
| Dict stream (ci) [0] | Dict checksum | Uncompressed dict checksum [2] |
+======================+===============+================================+

+==================+===============================+
| Dict length (ci) | Uncompressed dict length (ci) |
+==================+===============================+

[+=======================+================+=================================+
[| Chunk stream (ci) [0] | Chunk checksum | Uncompressed chunk checksum [2] |
[+=======================+================+=================================+

+===================+==========================+]
| Chunk length (ci) | Uncompressed length (ci) |] ...
+===================+==========================+]


The signatures:
+======================+
| Signature count (ci) |
+======================+

[+=====================+=====================+===========+]
[| Signature type (ci) | Signature size (ci) | Signature |] ...
[+=====================+=====================+===========+]


After the header, we have the body, which has the following:
+=================+
| Compressed Dict |
+=================+

[+===========================+]
[|           Chunk           |] ...
[+===========================+]


 

Design Delta Update in SWUpdate

artifact를 ZCK 형식으로 생성한 다음 ZCK의 헤더(format에 설명된 대로)를 추출하여 SWU에 추가할 수 있습니다. 이러한 방식으로 ZCK 파일은 SWU의 일부로 서명(요청 시 압축 및/또는 암호화)되며, 해당 hash가 헤더의 일부로 이미 확인되었으므로 외부 URL에서 chunk를 로드하는 것도 확인할 수 있습니다.

Integration in sw-description

Zchunk 파일에서 가장 중요한 부분은 헤더입니다. 헤더에는 비교를 수행하기 위한 모든 metadata와 hash가 포함되어 있습니다. zck 도구는 파일을 chunk로 분할하고 헤더를 생성합니다. 헤더의 크기는 알 수 있으며 헤더 자체는 ZCK 파일에서 추출할 수 있습니다. 헤더는 sw-description의 일부가 될 것입니다. 헤더는 전체 파일 크기에 비해 매우 작기 때문에(약 1%) 이 헤더는 SWU로 전달될 수 있습니다.

Integration in SWUpdate: the delta handler

delta handler는 artifact를 직접 설치하지 않고, stream을 생성하여 디바이스에 최종 설치를 담당하는 다른 (체인화된) 핸들러로 전달합니다.
delta handler는 간단히 말해 다음과 같은 임무를 수행합니다:

  • parse and understand the ZCK header
  • create a ZCK header from the file / partition used as source for the comparison
  • detect which chunks are missing and which one must be copied.
  • build a mixer that copies and downloads all chunks and generates a stream for the following handler.
  • detect any error coming form the chained handler.

delta handler는 더 많은 데이터를 다운로드해야 하므로 원본 ZCK가 저장된 스토리지에 대한 연결을 구성해야 합니다. 이는 핸들러가 하드웨어에 직접 쓰기 위해 높은 권한으로 실행되기 때문에 보안 문제로 이어질 수 있습니다. 이는 SWUpdate 설계의 일부인 privilege separation를 위반합니다. 이를 방지하기 위해 delta handler는 자체적으로 다운로드를 하지 않고, 다른 userid와 groupid로 실행되는 별도의 프로세스가 이를 담당합니다. 핸들러는 다운로드해야 하는 범위 목록과 함께 이 프로세스에 요청을 보냅니다(HTTP Range request). delta handler는 chunk가 다운로드되는 방법을 알지 못하며, HTTP Range Request을 사용하는 것이 가장 많이 선택되지만, 추가 구현방식에도 열려있습니다. downloader process는 서버에 연결하고 range를 요청합니다. 서버가 range를 제공할 수 없는 경우 업데이트가 중단됩니다. 실제로 delta update의 필수 요건은 ZCK 파일을 저장하는 서버가 HTTP 범위 요청에 응답할 수 있어야 하며, 전체 파일을 다운로드할 수 있는 폴백이 없어야 합니다. delta handler와 downloader process 사이에 간편한 IPC가 구현됩니다. 이를 통해 메시지를 교환할 수 있으며, 다운로더는 오류가 발생하면 핸들러에 알려 업데이트를 중지할 수 있습니다. 다운로더는 모든 chunk가 다운로드되면 종료 메시지를 보냅니다. 누락된 chunk의 수가 매우 많을 수 있으므로 delta handler는 다운로더에 여러 요청을 전송하고 정리한 후 각 요청을 추적해야 합니다. 다운로더는 연결을 시작하고 HTTP 헤더와 데이터를 검색하여 호출자에게 다시 전송하는 더미 서버로 생각하면 됩니다. 그러면 delta handler는 응답을 파싱하고 multipart HTTP body에서 누락된 chunk를 검색하는 역할을 담당합니다.

실제 동작 테스트

두개의 zck 파일을 만듭니다.
하나는 현재의 SW 인 v100, 업데이트할 SW 인 v101입니다.

zck -o rootfs.zck -u -h sha256 rootfs.ext4
HSIZE=`zck_read_header -v rootfs.zck | grep "Header size" | cut -d ':' -f 2`
dd if=rootfs.zck of=rootfs.zck.header bs=1 count=$((HSIZE))
$ ls v100/
rootfs.zck rootfs.zck.header
$ ls v101/
rootfs.zck rootfs.zck.header

전체 이미지 중 rootfs 바이너리 파일만 적용하였습니다.

 

두 개의 파일을 비교해 봅니다.

 

원본의 3% 정도만 업데이트가 필요한 상황입니다.
서버는 아파치를 사용하였고 다운로드 대상인 v101/rootfs.zck 파일은 /var/www/html/swupdate/rootfs.zck에 저장했습니다.
sw-description 은 아래와 같이 설정합니다.

images: (
    {
        filename = "rootfs.zck.header";
        type = "delta";

        device = "/dev/mmcblk0p2";
        properties: {
                url = "http://testserver.com/swupdate/rootfs.zck";
                chain = "ext4";
                source = "/dev/mmcblk0p3";
                zckloglevel = "error";
                /* source-size = "detect"; */
                /* max-ranges = ""; */
                /* debug-chunks = "true"; */
        };
    },
)

참고: https://sbabic.github.io/swupdate/handlers.html#delta-update-handler

type 값을 통해 핸들러는 delta handler가 선택됩니다.
device의 데이터와 filename의 데이터를 비교하여 range request를 생성합니다.
url의 서버에서 받은 데이터와 조합하여 v101rootfs.zck를 완성하여 ext4 handler에게 전달합니다.

[INFO ] : SWUPDATE running :  [print_registered_handlers:59] : Registered handlers:
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         dummy
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         uboot
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         bootloader
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         delta
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         raw
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         rawfile
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         rawcopy
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         ext4
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         shellscript
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         postinstall
[INFO ] : SWUPDATE running :  [print_registered_handlers:61] :         preinstall
[TRACE] : SWUPDATE running :  [network_initializer:566] : Main loop daemon
[TRACE] : SWUPDATE running :  [start_delta_downloader:155] : Starting Internal process for downloading chunks
[TRACE] : SWUPDATE running :  [install_single_image:229] : Found installer for stream rootfs.zck.header delta
[INFO ] : SWUPDATE running :  [get_total_size:301] : Total bytes to be reused     :    525297185
[INFO ] : SWUPDATE running :  [get_total_size:302] : Total bytes to be downloaded :      9448528
[INFO ] : SWUPDATE running :  [install_delta:1084] : Size of artifact to be installed : 563172532
[TRACE] : SWUPDATE running :  [trigger_download:586] : Range request : 902182-1064119,3184423-3229271,4651059-4655421,21067751-21110464,21113949-21156229,21388445-21397030,22132730-22133844,22134355-22136351,22250457-22291734,22312458-22320358, ...
[INFO ] : SWUPDATE running :  [install_delta:1142] : Total downloaded data : 9462600 bytes
[INFO ] : SWUPDATE successful !