1. 소개
이 절은 비규범적입니다.
Aqueous 아카이브 형식은 디렉터리 트리 또는 단일 파일을 블록의 시퀀스로 저장합니다. 이 절은 형식의 각 부분이 어떻게 서로 맞물리는지 개관합니다. 각 단락은 그 단락이 설명하는 부분을 정의하는 장을 가리킵니다.
아카이브는 헤더, 블록의 시퀀스, 푸터로 이루어집니다(§ 3 컨테이너). 모든 블록은 자신의 유형과 크기로 시작하므로 압축 해제기는 어떤 블록이든 이해하지 않고도 건너뛸 수 있으며, 유형은 블록을 이해하지 못할 때 그 블록을 무시할 수 있는지를 알려 줍니다(§ 3.1.1 블록 유형). 푸터는 마지막 메타데이터 블록을 거슬러 가리키므로, 압축 해제기는 아카이브의 끝에서부터 읽기 시작할 수 있습니다.
블록은 세그먼트로 묶이며, 각 세그먼트는 메타데이터 블록으로 끝납니다(§ 4 세그먼트와 엔트리). 메타데이터 블록은 세그먼트의 엔트리, 즉 디렉터리, 파일, 심볼릭 링크 및 기타 노드의 이름을 지정하고, 각 파일이 그 내용을 담은 블록을 가리키게 합니다. 나중 세그먼트는 앞선 세그먼트의 엔트리를 추가, 교체, 제거하므로, 아카이브는 앞부분을 다시 쓰지 않고 세그먼트를 덧붙이는 것만으로 확장할 수 있습니다. 메타데이터 블록이 전혀 없는 아카이브는 gzip 파일과 마찬가지로 이름 없는 단일 파일로 간단히 압축 해제됩니다.
파일의 내용은 단 하나의 리프 블록으로 주어집니다. 가장 단순한 리프 블록은 내용 자체를 저장하는 데이터 블록이며, 선택적으로 Zstandard와 여러 작은 블록이 공유하는 사전으로 압축될 수 있습니다(§ 5 저장된 데이터). 공식 블록은 내용을 저장하는 대신, 뒤따르는 비리프 블록에 대한 작은 스택 기계 프로그램으로 내용을 기술하는 리프 블록입니다(§ 6 공식). 이 프로그램은 그 블록들의 조각을 취하고, 이어 붙이고, 패치하며, 압축기가 수행한 변환을 되돌리기 위해 그 블록들에 WebAssembly 모듈을 적용합니다(§ 7 모듈). 리프 블록과 그 뒤를 따르는 비리프 블록은 파셀을 이루며, 이는 보통 압축 해제기가 단일 파일을 추출하기 위해 읽는 전부이고, 공식 블록은 그 안에서의 임의 접근을 위한 색인 역할을 합니다.
오버레이 아카이브는 디렉터리 트리를 베이스라고 부르는 기존 디렉터리 트리에 대한 변경으로 기술합니다(§ 8 오버레이 아카이브와 임포트). 임포트 블록은 베이스에 있는 파일의 바이트 범위를 읽으며 공식은 이를 패치할 수 있으므로, 오버레이 아카이브는 베이스의 업데이트 또는 델타로 동작합니다.
아카이브 전체는 age [AGE]를 사용하여 패스프레이즈로 암호화될 수 있습니다(§ 9 암호화 아카이브). 체크섬은 선택적으로 제외되는 리프 데이터 블록과, 등록 사전과 모듈을 해시로 지정하는 약칭 블록을 제외한 모든 블록을 대상으로 합니다(§ 4.2.2 등록 사전과 등록 모듈). 미러 블록은 손상되면 많은 파일에 영향을 미칠 블록의 사본을 담습니다(§ 10 무결성과 복구). 타임스탬프, 주석, 권한처럼 파일을 읽는 데 필요하지 않은 메타데이터는 CBOR로 인코딩되며 압축 해제기가 무시할 수 있습니다(§ 11 선택적 메타데이터, § 12 추출).
2. 규약
2.1. 식
모든 식은 표준 산술에 따라 해석되며, 다음과 같은 확장이 적용됩니다.
-
a / b는 floor(a ÷ b)와 같습니다.
-
b > 0일 때 a % b는 a를 b로 나눈 음이 아닌 나머지이며, a - (a / b) × b와 같습니다.
-
a << b는 a × 2b와 같습니다.
-
a >> b는 a / 2b와 같습니다.
-
XX16은 16진수 정수를 나타냅니다.
-
(a, b, …)는 값들의 순서 있는 배열을 나타냅니다.
-
(XX YY ZZ)16은 (XX16, YY16, ZZ16)의 약칭입니다.
-
a || b는 두 배열 a와 b를 연결하며, 배열이 아닌 값은 단일 원소 배열 (a) 또는 (b)로 취급합니다.
-
배열 a에 대해, n > 0일 때 an은 a || an-1과 같습니다. a0은 빈 배열 ()과 같습니다.
-
[a, b]는 a와 b 사이의 닫힌 구간을 나타내며, 양 끝을 모두 포함합니다.
-
[a, b)는 a(포함)와 b(제외) 사이의 반열린 구간을 나타냅니다.
모든 식의 결과는 -(264 - 1)과 264 - 1 사이의 정수이거나, 원소의 개수가 264 - 1을 넘지 않는 그러한 정수들의 배열입니다. 단, 하위 식은 여전히 이 범위를 넘을 수 있다는 점에 유의하십시오.
2.2. 타입
타입은 값들의 집합과 그에 연관된 인코딩 방식입니다. 타입이나 구조체의 바이트 길이를 문맥을 통해서만 알 수 있을 때, 그 타입이나 구조체는 암묵 크기입니다. 암묵 크기라고 명시되지 않는 한, 모든 타입은 문맥과 무관하게 정해진 바이트 길이를 가집니다.
u8은 1바이트를 차지하는 부호 없는 8비트 정수입니다. 그 범위는 [0, 28)입니다.
u16le는 2바이트를 차지하며 리틀 엔디언 바이트 순서(최하위 바이트가 먼저)로 저장되는 부호 없는 16비트 정수입니다. 그 범위는 [0, 216)입니다.
u32le는 4바이트를 차지하며 리틀 엔디언 바이트 순서로 저장되는 부호 없는 32비트 정수입니다. 그 범위는 [0, 232)입니다.
u64le는 8바이트를 차지하며 리틀 엔디언 바이트 순서로 저장되는 부호 없는 64비트 정수입니다. 그 범위는 [0, 264)입니다.
u(n)(n은 264보다 작은 정수)는 n > 0이면 n을 표현할 수 있는 가장 작은 정수 타입이고, n = 0이면 크기가 0인 타입입니다. 다시 말해 다음과 같습니다.
-
n = 0이면 u(n)은 크기가 0입니다.
-
n ∈ [1, 28)이면 u(n)은 u8과 같습니다.
-
n ∈ [28, 216)이면 u(n)은 u16le와 같습니다.
-
n ∈ [216, 232)이면 u(n)은 u32le와 같습니다.
-
n ∈ [232, 264)이면 u(n)은 u64le와 같습니다.
leb128은 LEB128 인코딩을 사용하여 가변 개수의 바이트로 인코딩된 부호 없는 64비트 정수입니다. 마지막 바이트를 제외한 각 바이트는 최상위 비트(MSB)가 1로 설정되어 있으며, 이는 뒤에 바이트가 더 이어짐을 나타냅니다. 각 바이트의 나머지 7비트는 정수의 값을 저장하는 데 사용됩니다. 첫 번째 바이트의 하위 7비트는 정수의 최하위 비트들이며, 이후 바이트도 같은 방식으로 이어집니다. 마지막 바이트는 MSB가 0으로 설정됩니다. 범위는 [0, 264)입니다. 인코딩은 최대 10바이트 길이여야 하며, 264 이상의 값을 인코딩해서는 안 됩니다.
a[n]은 a의 순서 있는 배열을 나타내며, 여기서 n은 정수입니다. n의 범위는 [0, 264)입니다.
a[*]는 원소 개수가 지정되지 않은 a의 암묵 크기 순서 있는 배열을 나타냅니다. 원소의 개수는 문맥으로부터 암묵적으로 결정됩니다. 이 명세는 이러한 문맥을 두 가지 사용합니다.
-
바이트 길이가 정의된 구조체에 하나 이상의 a[*] 필드가 있는 경우, 해당 필드들의 원소 개수는 요구되는 바이트 길이를 정확히 채우도록 필요한 만큼 늘어납니다. 이러한 조건을 만족하는 원소 개수의 조합은 단 하나여야 합니다.
-
제약 조건과 함께 a[*] 필드가 주어지면, 그 필드는 요구되는 제약 조건을 만족하지 않는 첫 번째 a 인스턴스 직전까지 이어지며, 그 인스턴스는 포함하지 않습니다.
2.3. 구조체
구조체는 서로 다른 타입일 수 있는 하나 이상의 순서 있는 필드를 이어 붙인 논리적 묶음입니다. 각 필드는 다음과 같은 속성을 가집니다.
-
필드의 설명적인 이름(예: size, compression_params).
-
필드의 존재 여부, 값 또는 해석을 결정하는 선택적인 기준 또는 조건.
필드 이름은 필드의 타입이 조건에 따라 달라지는 경우에만 겹칠 수 있으며, 이 경우 어느 시점에서든 그러한 조건 중 최대 하나만 적용될 수 있습니다. 필드의 이름이 이전 필드의 이름과 같은 경우 이름을 생략할 수 있습니다.
필드 타입은 offset일 수 있으며, 이는 지정되지 않은 파일 위치(원점)를 기준으로 한 다음 바이트까지의 오프셋으로 해석되는 특수한 크기 0의 타입입니다. 오프셋은 필드 집합 내의 바이트 수를 측정하는 데에만 사용됩니다. 이 명세의 구문과 의미는 원점의 선택에 의존하지 않습니다.
선택적인 제약 조건은 필드의 인코딩에 영향을 주지 않으며, 가능한 값의 집합을 좁히기만 합니다. 제약 조건은 다음 중 하나일 수 있습니다.
-
a ∈ X는 값이 지정된 집합 X에 속할 것을 요구합니다.
-
a ∉ X는 값이 지정된 집합 X에 속하지 않을 것을 요구합니다.
-
a = x는 값이 정확히 주어진 값 x여야 함을 요구합니다. x가 상수 표현식이면, 이는 사실상 이 필드의 바이트 인코딩을 고정합니다.
-
a ≠ x는 값이 주어진 값 x가 아니어야 함을 요구합니다.
조건은 다음 중 하나일 수 있습니다:
-
"선택"은 적합한 구현이 필요에 따라 해당 필드를 포함하거나 제외할 수 있음을 나타냅니다.
-
"조건 …"은 지정된 조건이 참인 경우에만, 그리고 그 경우에는 반드시 해당 필드를 포함해야 함을 나타냅니다.
-
"의존 대상 …"은 해당 필드의 해석이 지정된 다른 필드 집합에 따라 달라짐을 나타냅니다.
구조체는 그 바이트 길이를 문맥을 통해 알 수 없고 그 안에 암묵 크기 필드가 하나 이상 존재하는 경우에만 암묵 크기입니다.
2.4. 상수
이 명세는 다음 상수를 정의하고 사용하며, 이들은 모두 대문자로 표기합니다.
| 이름 | 값 |
|---|---|
| HEADER_TYPE | 27816 |
| OVERLAY_HEADER_TYPE | 6916 |
| FOOTER_TYPE | 90 5BCB 957D16 |
| OVERLAY_FOOTER_TYPE | 7FC16 |
| ENCRYPTED_HEADER_TYPE | 155 04DC A57916 |
| HEADER_MAGIC | (F8 04 03 6A D6 BE)16 |
| OVERLAY_HEADER_MAGIC | (69 0D 15 AF F6 B2)16 |
| FOOTER_MAGIC | (FD AA AE DE 85 12)16 |
| OVERLAY_FOOTER_MAGIC | (FC 0F 6E 7B 4B AC)16 |
| ENCRYPTED_HEADER_MAGIC | (F9 CA F2 A6 D0 2A 01 00)16 |
| ENCRYPTED_MIRRORED_HEADER_MAGIC | (F9 CA F2 A6 D0 2A 02 00)16 |
| MIRROR_MAGIC | (02 AA F5 FE BA 2B AE 23)16 |
| OVERLAY_MIRROR_MAGIC | (01 05 34 FA BD 11 AF 53)16 |
| ENCRYPTED_MIRROR_MAGIC | (78 D7 3F 69 0A FF 44 83)16 |
2.5. 의미론과 실패
이 명세는 아카이브의 의미를 바이트열에서 아카이브 값으로 가는 함수로 정의하며, 이 함수는 오버레이 아카이브의 경우 베이스도 함께 받습니다. 이 명세의 모든 알고리즘은 선언적입니다. 즉, 결과가 무엇인지를 정의할 뿐 그것을 어떻게 또는 어떤 순서로 계산해야 하는지를 정의하지 않으며, 적합한 구현은 같은 결과를 내는 다른 어떤 방식으로든 그것을 계산할 수 있습니다.
아카이브 해석의 결과는 아카이브 값 또는 실패 중 하나이며, 실패는 다음 중 하나입니다.
-
잘못된 아카이브 실패는 입력이 이 명세의 요구 사항 중 어느 하나라도 위반할 때 발생합니다. 이는 오직 입력만의 속성이며, 모든 적합한 압축 해제기는 이에 대해 일치합니다.
-
리소스 실패는 평가가 선언된 한도 또는 구현에서 정의한 한도를 초과할 때 발생합니다. 이는 입력의 속성이 아니며, 적합한 압축 해제기들은 이에 대해 서로 다른 결과를 낼 수 있습니다.
-
복호화 실패는 암호화 아카이브를 주어진 패스프레이즈(있는 경우)로 복호화할 수 없을 때 발생합니다. 이는 입력과 패스프레이즈의 속성이며, 모든 적합한 압축 해제기가 이에 대해 일치합니다.
-
베이스 실패는 오버레이 아카이브가 베이스 없이 해석되거나, 임포트를 주어진 베이스에서 해석할 수 없을 때(§ 8.2 임포트 해석) 발생합니다. 이는 입력과 베이스의 속성이며, 모든 적합한 압축 해제기가 이에 대해 일치합니다.
-
미지원 실패는 입력에 이 명세에서 정의하지 않은 필수 유형의 블록(§ 3.1.1.1 예약된 블록 유형)이나, 압축 해제기가 알지 못하는 등록 해시를 가진 약칭 블록(§ 4.2.2 등록 사전과 등록 모듈)이 포함되어 있을 때 발생합니다. 이는 입력, 이 명세의 버전, 그리고 알려진 레지스트리 엔트리의 속성이며, 같은 버전을 구현하고 같은 엔트리를 알고 있는 모든 적합한 압축 해제기가 이에 대해 일치합니다.
적합한 압축기는 아카이브 값이 주어지면 그 해석이 해당 아카이브 값인 바이트열을 생성해야 합니다. 압축기가 오버레이 아카이브를 생성할 때는 베이스도 함께 주어지며, 그 베이스를 사용한 해석은 해당 아카이브 값이어야 합니다. 이 명세는 압축기가 블록, 블록 크기, 사전 또는 공식을 선택하는 방식을 제한하지 않습니다.
3. 컨테이너
3.1. 블록
블록은 유형이 지정된 바이트열입니다. 암호화되지 않은 아카이브 전체와 암호화 아카이브의 시작 부분은 블록 또는 블록과 유사한 구조로 구성됩니다.
블록은 다음과 같은 일반 구조를 가집니다:
| 이름 | 타입 | 조건 |
|---|---|---|
| block_start | offset | |
| type | leb128 ∉ {FOOTER_TYPE, OVERLAY_FOOTER_TYPE} | |
| size | u8 | type % 4 = 0인 경우 |
| u16le | type % 4 = 1인 경우 | |
| u32le | type % 4 = 2인 경우 | |
| u64le | type % 4 = 3인 경우 | |
| data | u8[size] | type에 따라 다름 |
| block_end | offset | |
| padding | u8 | (block_end - block_start) % 2 = 1인 경우 |
FOOTER_TYPE과 OVERLAY_FOOTER_TYPE을 포함한 일부 type 값은 블록과 유사한 구조를 나타내는 데에도 사용됩니다. 예를 들어 아카이브 푸터는 블록 뒤에 놓이기 위해 별개의 블록 유형을 가져야 합니다. 이 명세는 명시적으로 언급하지 않는 한 이러한 블록 유사 구조에 어떠한 의미도 부여하지 않습니다.
type은 가장 짧은 leb128 인코딩을 사용해야 합니다.
padding이 존재하므로 모든 블록의 바이트 수는 짝수가 됩니다. 적합한 압축기는 padding을 0으로 설정해야 합니다. 그러나 그 값은 아카이브 해석 결과에 영향을 주지 않습니다.
이후 절에서는 정의된 각 type에 따라 data 필드의 내용을 설명합니다. type은 size의 바이트 길이도 결정하므로, 일반적으로 각 논리적 블록 유형마다 여러 개의 type 값이 할당됩니다.
3.1.1. 블록 유형
다음 표는 블록 유형의 모든 범위를 나열합니다. 단일 논리적 블록 유형에 해당하는 블록 유형의 범위는 이진수로도 표기합니다. 예:0001 0mss 여기서 문자는 블록의 매개변수를 결정하는 비트를 나타냅니다. ss는 항상 최하위 두 비트를 나타내며, 이는 위에서와 같이 size의 너비를 결정합니다. 0은 u8, 1은 u16le, 2는 u32le, 3은 u64le입니다. 예약된 블록 유형이 필수인지 여부는 그 첫 자리 숫자에 의해 결정됩니다(§ 3.1.1.1 예약된 블록 유형).
16진수 2자리로 된 블록 유형은 다음과 같습니다.
| 블록 유형 | 필수 | 정의 |
|---|---|---|
| 0016 — 0316 | 예 | § 3.3 스페이서 블록 |
| 0416 | 예 | § 3.4 크기 힌트 블록 |
| 0516 — 0F16 | 예 | 예약됨 |
| 1016 — 1716 | 예 | § 4.3 메타데이터 블록 |
| 1816 — 1F16 | 예 | 예약됨 |
| 2016 — 3716 | 예 | § 5.1 데이터 블록 |
| 3816 — 3F16 | 예 | 예약됨 |
| 4016 — 4B16 | 예 | § 5.1 데이터 블록 |
| 4C16 — 4F16 | 예 | 예약됨 |
| 5016 | 예 | § 5.2 사전 블록, 약칭 블록으로서 (§ 4.2.2 등록 사전과 등록 모듈) |
| 5116 — 5716 | 예 | § 5.2 사전 블록 |
| 5816 — 5F16 | 예 | § 8.1 임포트 블록 |
| 6016 | 예 | § 7.1 모듈 블록, 약칭 블록으로서 (§ 4.2.2 등록 사전과 등록 모듈) |
| 6116 — 6316 | 예 | § 7.1 모듈 블록 |
| 6416 — 6716 | 예 | § 7.2 리소스 블록 |
| 6816 | 예 | 예약됨 |
| 6916 | 예 | OVERLAY_HEADER_TYPE용으로 예약됨 (§ 3.1.1.1 예약된 블록 유형) |
| 6A16 — 6F16 | 예 | 예약됨 |
| 7016 — 7716 | 예 | § 6.1 공식 블록 |
| 7816 — 9F16 | 예 | 예약됨 |
| A016 — CF16 | 아니요 | 예약됨 |
| D016 — DF16 | 아니요 | 개발 환경용으로 예약됨 |
| E016 — EF16 | 아니요 | 실험용으로 예약됨 |
| F016 — FF16 | 아니요 | 예약됨 |
16진수 3자리 이상의 블록 유형은 다음과 같습니다:
| 블록 유형 | 필수 | 정의 |
|---|---|---|
| 10016 — 9FF16, 100016 — 9FFF16, … | 예 | 예약됨, 블록과 유사한 구조 포함(§ 3.1.1.1 예약된 블록 유형) |
| A0016 — BFF16, A00016 — BFFF16, … | 아니요 | 예약됨 |
| C0016 — C0F16 | 아니요 | 예약됨 |
| C1016 — C1716 | 아니요 | § 4.3 메타데이터 블록용 § 10.3 미러 블록 |
| C1816 — C4F16 | 아니요 | 예약됨 |
| C5016 — C5716 | 아니요 | § 5.2 사전 블록용 § 10.3 미러 블록 |
| C5816 — C5F16 | 아니요 | § 8.1 임포트 블록용 § 10.3 미러 블록 |
| C6016 — C6316 | 아니요 | § 7.1 모듈 블록용 § 10.3 미러 블록 |
| C6416 — C6716 | 아니요 | § 7.2 리소스 블록에 대한 § 10.3 미러 블록 |
| C6816 — C6F16 | 아니요 | 예약됨 |
| C7016 — C7716 | 아니요 | § 6.1 공식 블록에 대한 § 10.3 미러 블록 |
| C7816 — CFF16, C00016 — CFFF16, … | 아니요 | 예약됨 |
| D0016 — DFF16, D00016 — DFFF16, … | 아니요 | 개발 환경용으로 예약됨 |
| E0016 — EFF16, E00016 — EFFF16, … | 아니요 | 실험용으로 예약됨 |
| F0016 — FFF16, F00016 — FFFF16, … | 아니요 | 예약됨 |
3.1.1.1. 예약된 블록 유형
블록 유형의 선행 자릿수는 최소 2자리가 되도록 0으로 채운 16진수 표현의 첫 번째 자릿수입니다. 예를 들어 416, 4B16, C1016의 선행 자릿수는 각각 0, 4, C입니다.
블록 유형은 선행 자릿수가 0부터 9까지이면 필수이고, A부터 F까지이면 비필수입니다. 이 명세에서 정의하지 않은 타입의 블록은 다음과 같습니다:
-
타입이 필수이면 미지원 실패(§ 4.4 아카이브 해석)가 됩니다;
-
그 외의 경우에는 무시 블록입니다. 미러 블록과 마찬가지로 무시 블록은 어떤 세그먼트에도 속하지 않으며, 블록 내 어디에나 나타날 수 있고, 아카이브 해석의 결과에 영향을 주지 않습니다.
타입의 선두 자릿수가 D인 블록(즉, D016부터 DF16까지, D0016부터 DFF16까지, …)은 개발 환경용으로 예약되어 있습니다. 이 명세는 이러한 블록에 어떠한 의미도 부여하지 않습니다. 이러한 블록은 이를 활성화하는 특정 명령이 있을 때에만 쓰거나 읽어야 합니다.
타입의 선두 자릿수가 E인 블록(즉, E016부터 EF16까지, E0016부터 EFF16까지, …)은 실험용으로 예약되어 있습니다. 이 명세는 그러한 블록에 어떠한 의미도 부여하지 않습니다.
다음 블록 유형은 블록과 유사한 구조로 사용되므로 유효하지 않은 것으로 정의됩니다. 유효한 아카이브는 이러한 블록 유형을 정식 블록으로 포함해서는 안 됩니다.
-
블록 27816(HEADER_TYPE)은 암호화되지 않은 아카이브 헤더에 사용됩니다.
-
블록 6916(OVERLAY_HEADER_TYPE)는 오버레이 아카이브 헤더에 사용됩니다.
-
블록 90 5BCB 957D16(FOOTER_TYPE)는 아카이브 푸터에 사용됩니다.
-
블록 7FC16(OVERLAY_FOOTER_TYPE)는 오버레이 아카이브 푸터에 사용됩니다.
-
블록 155 04DC A57916(ENCRYPTED_HEADER_TYPE)는 암호화 아카이브 헤더에 사용됩니다.
3.2. 공유 외부 구조
많은 블록 유형은 동일한 외부 구조를 공유하며, 내부 구조만 다릅니다. 이 구조는 압축될 수도 있고 압축되지 않을 수도 있으며, 상위 구조에서 지정한 compression_method와 checksum_method를 사용합니다. 각 압축 방식은 자신의 매개변수(compression_params)의 구조를 정의하며, 그 바이트 길이는 이를 파싱하여 결정됩니다. 각 checksum_method에는 또한 연관된 고정 checksum_size가 있습니다. 체크섬이 있는 Zstandard 데이터는 compression_method로 Zstandard를, checksum_method로 XXH64를 사용하는 압축된 외부 구조(compressed_data)입니다.
압축된 외부 구조(compressed_data)는 다음으로 구성됩니다:
| 이름 | 타입 | 조건 |
|---|---|---|
| original_size_delta | leb128 | |
| original_size_shrink | leb128 | original_size_delta = 0인 경우 |
| compression_params | compression_method의 매개변수 | compression_method에 따라 다름 |
| data_start | offset | |
| data | u8[*] | |
| data_end | offset | |
| checksum | u8[checksum_size] | checksum_method가 None이 아닌 경우, checksum_method에 따라 다름 |
압축된 크기(compressed_size)는 블록 크기로부터 추론합니다: compressed_size = data_end - data_start. 원본 크기(original_size)는 compressed_size에 대한 상대값으로 인코딩합니다:
-
original_size_delta가 0이 아니면, original_size = compressed_size + original_size_delta입니다.
-
그렇지 않으면 original_size = compressed_size - original_size_shrink이며, original_size_shrink는 compressed_size 이하여야 합니다.
따라서 각 원본 크기는 정확히 하나의 인코딩을 가지며, compressed_size와 같은 원본 크기는 두 개의 0 바이트로 인코딩됩니다.
압축되지 않은 외부 구조(uncompressed_data)는 다음으로 구성됩니다:
| 이름 | 타입 | 조건 |
|---|---|---|
| data | u8[*] | |
| checksum | u8[checksum_size] | checksum_method가 None이 아닌 경우 |
두 구조 모두에서 체크섬은 저장된 바이트(§ 10.1 체크섬)가 아니라 디코딩된 데이터(§ 5.3 블록 내용), 즉 압축 해제 후의 데이터를 대상으로 계산됩니다.
3.2.1. Zstandard 압축 방식
이 명세는 단 하나의 압축 방식인 Zstandard [RFC8878]만을 정의합니다. Zstandard 블록은 Zstandard 프레임의 "Block" 구조([RFC8878], Section 3.1.1.2)입니다. 그 매개변수(compression_params)는 다음과 같습니다.
| 이름 | 타입 |
|---|---|
| window_descriptor | u8 |
window_descriptor 필드의 의미는 Zstandard의 Window_Descriptor 바이트와 동일하며, 이 바이트는 [RFC8878], Section 3.1.1.1.2에서와 같이 Window_Size를 결정합니다. 따라서 각 Zstandard 블록은 최대 Block_Maximum_Size = min(Window_Size, 128 KiB) 바이트의 데이터를 담습니다.
적합한 압축 해제기는 227 바이트(128 MiB)까지의 모든 Window_Size를 지원해야 하며, 더 큰 Window_Size에 대해서는 리소스 실패를 반환할 수 있습니다. 적합한 압축기는 명시적으로 요청받지 않는 한 227 바이트보다 큰 Window_Size를 사용하지 않는 것이 좋습니다.
사전은 마치 데이터 앞에 놓인 것처럼 압축 방식에 제공되는 바이트열로, 일반적으로 공유 압축 컨텍스트가 없는 점을 보완하기 위해 여러 작은 블록이 공유합니다. 사전이 사용되는 경우, 그 내용, 엔트로피 테이블 및 반복 오프셋은 § 5.2 사전 블록에서 설명하는 바와 같이 사전 블록에 의해 주어집니다.
이 방식으로 압축된 데이터는 Zstandard 프레임이 제공하는 추가 프레이밍 없이 Zstandard 블록들을 나열한 것입니다. data는 다른 바이트 없이 하나 이상의 그러한 블록으로 구성되어야 하며, 마지막 블록만이 그 블록 헤더에 의해 최종 블록으로 표시됩니다.
3.2.2. XXH64 체크섬 방식
이 명세는 단 하나의 체크섬 방식인 XXH64 [XXHASH]만을 정의합니다. 그 시드는 0이어야 하며 결과는 u64le로 저장되어야 합니다. 그 결과 checksum_size는 8입니다.
3.3. 스페이서 블록
블록 016부터 316까지는 스페이서 블록입니다. 그 데이터는 다음으로 구성됩니다.
스페이서 블록은 2 이상의 임의의 짝수 바이트를 차지하며, 패딩으로 인해 모든 블록이 짝수 바이트를 차지하므로 이것으로 충분합니다.
적합한 압축기는 데이터 유출을 방지하기 위해 공간을 0 바이트로 채워야 하지만, 그 내용은 아카이브 해석 결과에 영향을 주지 않습니다. 적합한 압축기는 성능을 위해 희소 파일(sparse file)을 활용하는 것이 좋습니다.
3.4. 크기 힌트 블록
블록 416은 크기 힌트 블록입니다. 그 데이터는 다음으로 구성됩니다.
| 이름 | 타입 |
|---|---|
| size_hint | leb128 ≠ 0 |
이 블록이 존재하는 경우, 이 블록은 헤더 바로 다음의 첫 번째 블록이어야 하며 그 size_hint 필드는 푸터의 size_hint 필드와 같아야 합니다.
3.5. 암호화되지 않은 아카이브
암호화되지 않은 아카이브는 헤더, 0개 이상의 블록, 그리고 푸터로 구성됩니다.
| 이름 | 타입 | 조건 |
|---|---|---|
| header_magic | u8[6] ∈ {HEADER_MAGIC, OVERLAY_HEADER_MAGIC} | |
| archive_start | offset | |
| size_hint_block | size_hint_block | 선택 |
| blocks | 블록[*] | |
| archive_end | offset | |
| footer_magic | u8[6] ∈ {FOOTER_MAGIC, OVERLAY_FOOTER_MAGIC} | |
| footer_size | u16le = footer_end - footer_start | |
| footer_start | offset | |
| size_hint | leb128 = archive_end - archive_start | |
| metadata_backward_offset | leb128 | |
| footer_end | offset | |
| footer_size_copy | u16le = footer_size |
header_magic은 파일이 암호화되지 않은 Aqueous 아카이브임을 식별하기 위한 고정된 6바이트 바이트열입니다. 아카이브가 파일의 시작에서 시작하지 않는 경우, 이 헤더는 아카이브의 시작도 표시합니다.
footer_magic은 푸터의 시작을 표시하기 위한 고정된 6바이트 바이트열입니다. 아카이브가 파일의 끝에서 끝나지 않는 경우, 푸터는 아카이브의 끝도 표시합니다.
암호화되지 않은 아카이브는 header_magic이 OVERLAY_HEADER_MAGIC이면 오버레이 아카이브이고, 그렇지 않으면 독립 아카이브입니다. footer_magic은 오버레이 아카이브에서는 OVERLAY_FOOTER_MAGIC이어야 하고, 그 밖의 경우에는 FOOTER_MAGIC이어야 합니다. 오버레이 아카이브는 오버레이 아카이브와 별도로 주어지는 기존 디렉터리 트리 또는 파일인 베이스에 대해 해석됩니다. 즉 오버레이 아카이브는 빈 디렉터리 대신 베이스에서 시작하여 자신의 세그먼트를 베이스에 적용하며(§ 4.4 아카이브 해석), 베이스를 읽는 외부 임포트 블록은 오버레이 아카이브만 포함할 수 있습니다(§ 8.1 임포트 블록). 오버레이 아카이브의 형식은 Aqueous 오버레이 아카이브 형식이라고 하며, 고유한 미디어 타입을 가집니다(§ 15.2 application/aqo 미디어 타입).
header_magic과 footer_magic 사이의 오프셋은 size_hint로 기록되며, 임의 접근이 필요한 경우 size_hint_block으로도 복제할 수 있습니다.
푸터 크기, 즉 footer_size와 footer_size_copy 사이(둘 다 제외)에 있는 모든 필드의 크기는 아카이브의 끝에서 푸터를 읽을 수 있도록 footer_size와 footer_size_copy 양쪽에 기록됩니다(§ 3.6 아카이브 찾기).
metadata_backward_offset은 previous_metadata_offset이 현재 메타데이터 블록의 시작에서 이전 메타데이터 블록까지 거슬러 올라가는 오프셋인 것과 마찬가지로, footer_start에서 마지막 메타데이터 블록의 시작까지 거슬러 올라가는 오프셋입니다. 압축 해제기가 blocks를 스캔하지 않고도 메타데이터 블록이 없는 아카이브를 구별할 수 있도록, 이 값은 메타데이터 블록이 없는 경우에만, 그리고 그 경우에는 반드시 0이어야 합니다.
blocks는 논리적으로 세그먼트로 나뉘며, 각 세그먼트는 콘텐츠 블록과 이를 기술하는 하나의 메타데이터 블록으로 구성되고, 그 사이에 선택적인 스페이서 블록, 미러 블록, 무시 블록이 올 수 있습니다. 각 세그먼트는 읽는 순서대로 독립적으로 처리 및 압축 해제될 수 있습니다. 다만 내부 임포트(§ 8.1 임포트 블록)는 선행 세그먼트의 결과를 읽습니다. 세그먼트는 § 4 세그먼트와 엔트리에서 설명합니다.
또는 블록에 메타데이터 블록이 없을 수도 있습니다. 그러한 아카이브는 이름 없는 단일 파일로 압축 해제되며, 그 내용은 모든 리프 블록의 내용을 나타나는 순서대로 이어 붙인 것입니다. 리프 블록이 없으면 빈 파일로 압축 해제됩니다.
3.6. 아카이브 찾기
파일은 앞쪽 바이트(예: 자동 압축 풀림 실행 파일)나 뒤쪽 바이트(예: 전송 과정에서 덧붙여진 바이트)와 함께 아카이브를 담고 있을 수 있습니다. 아카이브 해석은 아카이브만을 입력으로 받으므로, 그러한 파일을 받아들이는 압축 해제기는 먼저 아카이브를 찾아야 합니다.
헤더 매직과 푸터 매직이 HEADER_MAGIC과 FOOTER_MAGIC이거나, OVERLAY_HEADER_MAGIC과 OVERLAY_FOOTER_MAGIC이면 두 매직이 일치한다고 합니다.
바이트열 file에서 아카이브를 찾으려면 다음과 같이 합니다.
-
file이 ENCRYPTED_HEADER_MAGIC 또는 ENCRYPTED_MIRRORED_HEADER_MAGIC으로 시작하면 file을 반환합니다.
-
back을 null로 둡니다. file의 마지막 2바이트를 u16le로 읽은 값을 n이라 하고, file의 길이 − n − 10을 f라 합니다. f ≥ 0이고, f부터의 file 바이트가 FOOTER_MAGIC 또는 OVERLAY_FOOTER_MAGIC 뒤에 u16le로 된 n이 이어진 것이며, f + 8부터의 leb128이 h = f − s − 6 ≥ 0을 만족하는 s이고 h부터의 file 바이트가 그 푸터 매직과 일치하는 헤더 매직이면, back을 h로 설정합니다.
-
file이 HEADER_MAGIC 또는 OVERLAY_HEADER_MAGIC으로 시작하면 다음과 같이 합니다.
-
back이 0이면 file을 반환합니다.
-
현재 오프셋 e의 바이트가 FOOTER_MAGIC 또는 OVERLAY_FOOTER_MAGIC으로 시작할 때까지, 오프셋 6부터 일반 구조(§ 3.1 블록)에 따라 블록을 읽되, 모든 블록의 data는 파싱하지 않고 바이트열로 취급합니다. 그 전에 블록을 읽는 데 실패하거나, 푸터 매직이 헤더 매직과 일치하지 않으면 실패를 반환합니다.
-
e + 6부터의 file의 2바이트를 u16le로 읽은 값을 m이라 합니다. file이 e + m + 10바이트보다 짧으면 실패를 반환합니다. 그렇지 않으면 file의 처음 e + m + 10바이트를 반환합니다.
-
-
back이 null이 아니면 back부터의 file 바이트를 반환합니다.
-
실패를 반환합니다.
찾은 아카이브는 이어서 해석되며, 이 과정에서 여전히 실패할 수 있습니다. 압축 해제기는 file에 찾은 아카이브 바깥의 바이트가 있으면 사용자에게 경고하는 것이 좋습니다. 압축 해제기는 다른 방법으로 아카이브를 찾을 수 없을 때, 예를 들어 file에 앞쪽 바이트와 뒤쪽 바이트가 모두 있을 때, HEADER_MAGIC과 OVERLAY_HEADER_MAGIC을 검색할 수 있습니다.
FOOTER_MAGIC과 OVERLAY_FOOTER_MAGIC은 유효한 블록 유형이 아닌 FOOTER_TYPE과 OVERLAY_FOOTER_TYPE의 가장 짧은 leb128 인코딩으로 시작하므로(§ 2.4 상수), 블록은 이들로 시작할 수 없습니다. 따라서 3.2단계는 file에서 첫 번째 아카이브의 끝을 찾고, 4단계는 그 앞의 바이트를 읽지 않고 file 끝에 있는 아카이브를 찾습니다. 서로 이어 붙인 아카이브는 하나의 아카이브가 아니며, 그중 첫 번째 아카이브만 찾아집니다.
4. 세그먼트와 엔트리
4.1. 아카이브 모델
-
content: 바이트열입니다.
-
link group: 링크 그룹 또는 null입니다.
-
extra:
aqr-entry-extra에 부합하는 CBOR 데이터 항목 또는 null입니다.
-
children: 엔트리 이름에서 파일 노드, 디렉터리 노드, 링크 노드, 특수 노드 또는 미지 노드로의 순서 있는 맵입니다.
-
extra:
aqr-entry-extra에 부합하는 CBOR 데이터 항목 또는 null입니다.
링크 노드는 심볼릭 링크를 나타내며, 다음 항목을 가지는 구조체입니다.
-
target: 링크 대상입니다.
-
extra:
aqr-entry-extra에 부합하는 CBOR 데이터 항목 또는 null입니다.
특수 노드는 FIFO 또는 장치 파일을 나타내며, 다음 항목을 가지는 구조체입니다.
-
kind: "
fifo", "char-device", "block-device" 중 하나입니다. -
device: kind가 "
char-device" 또는 "block-device"이면 음이 아닌 정수의 쌍 (major, minor)이고, 그렇지 않으면 null입니다. -
extra:
aqr-entry-extra에 부합하는 CBOR 데이터 항목 또는 null입니다.
makedev가 받는 방식처럼 플랫폼별 dev_t 인코딩으로 저장되지 않고, 쌍으로 저장됩니다. 그 의미는 이를 추출하는 플랫폼에 달려 있습니다. 같은 쌍이라도 플랫폼이 다르면 일반적으로 서로 무관한 장치를 가리키며(예를 들어 /dev/null은 Linux에서는 (1, 3)이지만 macOS에서는 (3, 2)입니다), 이 명세는 그 번호가 어느 플랫폼에서 왔는지 기록하지 않습니다.
미지 노드는 이 명세의 이번 버전에서 정의되지 않은 종류의 노드를 나타내며, 다음 항목을 가지는 구조체입니다.
-
kind: 4보다 큰 정수입니다.
-
data: 바이트열입니다.
-
extra:
aqr-entry-extra에 부합하는 CBOR 데이터 항목 또는 null입니다.
root는 링크 노드, 특수 노드, 미지 노드일 수 없습니다.
툼스톤은 항목이 없는 구조체로, 노드의 제거를 표시합니다. 툼스톤은 세그먼트 해석 결과의 children에만 나타나며, 앞선 결과에 병합될 때 제거되므로(§ 4.4.2 세그먼트 병합) 아카이브 값에는 절대 나타나지 않습니다.
두 엔트리 이름은 두 항목이 모두 같은 경우에만 같습니다. 특히 "wtf-8" 이름과 "raw" 이름은 바이트가 같더라도 결코 같지 않습니다.
엔트리 이름의 바이트는 0016이나 2F16(/)를 포함할 수 없으며, (2E)16(.)이나 (2E 2E)16(..)와 같을 수도 없습니다.
엔트리 이름은 다음 방법 중 하나로 바이트열로 인코딩되며, 어느 방법도 0 바이트를 포함하지 않습니다:
적합한 압축기는 "wtf-8" 인코딩을 사용하는 것이 권장되며, "raw" 인코딩은 왕복 변환을 위한 수단으로만 사용하는 것이 좋습니다.
entry_name 필드 bytes에서 엔트리 이름을 디코딩하려면:
-
bytes가 비어 있지 않고 bytes[0]이 C116이면, name을 인코딩이 "
raw"이고 바이트가 첫 번째 바이트를 제외한 bytes인 엔트리 이름으로 둡니다. -
그렇지 않고 bytes가 유효한 WTF-8 문자열이면, name을 인코딩이 "
wtf-8"이고 바이트가 bytes인 엔트리 이름으로 둡니다. -
그렇지 않으면 잘못된 아카이브 실패를 반환합니다.
-
name을 반환합니다.
-
인코딩, "
wtf-8" 또는 "raw" 중 하나입니다. -
bytes: 0016을 포함하지 않는 비어 있지 않은 바이트열로, 인코딩이 "
wtf-8"이면 유효한 WTF-8 [WTF-8] 문자열입니다.
엔트리 이름과 달리 링크 대상은 구분자로 2F16(/)를 포함할 수 있고, .이나 ..와 같은 구성 요소를 포함할 수 있는 경로입니다. 링크 대상은 절대 경로일 수 있으며, 아카이브 값이나 다른 어느 곳에도 존재하지 않는 경로를 가리킬 수 있습니다. 구분자는 항상 /이며, Windows처럼 \를 구분자로 사용하는 플랫폼에서 온 링크 대상이라도 마찬가지로, 이 경우 모든 \는 /로 표현됩니다. 그 외에는 링크 대상을 정규화하지 않으며, 그 의미(예: 드라이브 문자의 의미)는 플랫폼에 맡깁니다. 구현에서 정의한 제한 외에는 길이 제한이 없습니다.
/는 Windows에서 파일 이름에 나타날 수 없고 그곳에서는 링크 대상의 구분자로 해석되지 않으므로, 이 치환은 손실이 없습니다. Windows의 압축 해제기는 링크를 생성할 때 /를 다시 \로 치환합니다(§ 12 추출).
Windows에서 절대 링크 또는 정션의 대상은 \??\로 시작하는 NT 경로로 저장되며, 이는 다른 플랫폼에서는 아무런 의미가 없습니다. 압축기는 이를 먼저 Win32 경로로 변환하는 것이 권장됩니다. 예를 들어 \??\C:\foo는 C:/foo로, \??\UNC\server\share\x는 //server/share/x로, \??\Volume{GUID}\x는 //?/Volume{GUID}/x로 변환합니다.
링크 대상의 최대 길이는 플랫폼마다 다릅니다. Linux에서는 4095바이트(일부 파일 시스템에서는 더 짧으며, 예를 들어 XFS에서는 1023바이트), macOS와 FreeBSD에서는 1023바이트, Windows에서는 약 8,000개의 UTF-16 코드 단위입니다. 생성할 수 없는 링크는 추출 시점에 처리됩니다(§ 12 추출).
link_target 필드 bytes에서 링크 대상을 디코딩하려면, bytes에서 엔트리 이름을 디코딩할 때와 같이 하십시오. 단, 결과는 링크 대상이며 그 대신 링크 대상의 제약이 적용됩니다.
링크 대상은 아카이브 모델 안에서 결코 해석되지 않습니다. 아카이브 값의 모든 경로는 여전히 단순한 구성 요소의 나열이며, 아카이브 값에서 경로를 조회할 때는 lstat가 그러하듯 링크 노드를 결코 따라가지 않습니다. 특히 어떤 노드도 링크 노드의 하위 노드가 아닙니다.
/etc를 가리키는 앞선 링크 a를 통해 a/passwd를 추출하는 고전적인 공격은 아카이브 모델에서 표현할 수 없습니다. 도구는 링크를 따라가는 조회를 제공할 수 있습니다. 다만 그 경우 따라가는 링크의 수를 제한해야 합니다.
링크 그룹은 동등성 비교에만 쓰이는 불투명한 값이며, 세그먼트와 세그먼트 로컬 정수의 쌍이거나 아카이브 전역 정수입니다(§ 11.3 link-group: 하드 링크). 두 아카이브 값은 둘 중 하나의 링크 그룹을 어떤 일대일 방식으로 이름을 바꾸어 동일해지는 경우에만, 그리고 그 경우에 한하여 같습니다.
4.2. 콘텐츠 블록
스페이서, 크기 힌트, 메타데이터, 미러 및 무시 블록을 제외한 모든 블록은 콘텐츠 블록이며, 이는 뒤따르는 메타데이터 블록에 의해 기술되어야 합니다. 메타데이터 블록과, 이전 메타데이터 블록(없으면 아카이브의 시작)과 그 메타데이터 블록 사이의 모든 콘텐츠 블록은 하나의 세그먼트를 이룹니다. 예외적으로, 메타데이터 블록이 없는 아카이브(§ 3.5 암호화되지 않은 아카이브)는 메타데이터 블록이 없는 단일 세그먼트를 이룹니다. 각 세그먼트는 자기 완결적이어야 합니다. 세그먼트 안의 블록에서 나오는 모든 참조는 인덱스에 의한 것이든 오프셋에 의한 것이든 같은 세그먼트 안의 블록을 가리켜야 합니다. 내부 임포트(§ 8.1 임포트 블록)는 블록이 아니라 경로를 통해 선행 세그먼트의 노드를 참조하며, 세그먼트가 다른 세그먼트의 내용을 사용할 수 있는 유일한 방법입니다.
콘텐츠 블록은 다음과 같이 분류됩니다.
-
리프 블록은 그 자체로 하나 이상의 파일의 콘텐츠를 나타냅니다. 리프 데이터 블록과 공식 블록은 리프 블록입니다.
-
비리프 블록은 그 자체로는 어떤 파일도 나타내지 않으며, 하나 이상의 공식 블록이 참조하는 데이터를 공급하기만 합니다. 비리프 데이터 블록과 임포트 블록은 비리프 블록입니다. 각 비리프 블록은 공식 블록에 하나 이상의 비리프 항목을 공급합니다. 비리프 데이터 블록은 단일 항목을 공급하고, 임포트 블록은 자신의 각 임포트마다 항목 하나를 공급합니다.
-
보조 블록은 다른 블록을 디코딩하는 데 사용되는 데이터를 공급합니다. 사전 블록, 모듈 블록 및 리소스 블록은 보조 블록입니다.
사전, 모듈 및 리소스 블록은 각각 세그먼트 안에서 나타나는 순서대로 인덱스가 매겨집니다. 이 인덱스에는 두 가지 규칙이 사용됩니다.
-
콘텐츠 블록의 필드는 0부터 시작하는 인덱스를 사용합니다. 참조의 존재 여부가 별도로(예: 블록 유형으로) 표시되기 때문입니다.
-
메타데이터 블록에서 참조의 부재를 나타낼 수 있는 필드는 1부터 시작하는 인덱스를 사용하며, 이때 0은 부재를 나타냅니다.
4.2.1. 블록 순서
세그먼트 안의 블록은 공식 블록이 그보다 앞선 블록들에 의해 해석되고, 그 공식 블록을 평가하는 데 필요한 모든 데이터가 그 뒤에 오도록 정렬되어야 합니다.
-
사전 블록은 그것을 사용하는 모든 블록보다 앞에 와야 합니다.
-
모듈 블록 또는 리소스 블록은 그것을 참조하는 모든 공식 블록보다 앞에 와야 합니다.
-
모든 비리프 항목은 하나 이상의 공식 블록에 의해 참조되어야 하며, 모든 비리프 블록은 그 항목을 참조하는 모든 공식 블록보다 뒤에 와야 합니다.
-
리프 블록은 그 밖에는 제약을 받지 않으며 비리프 블록 사이에 끼워 넣을 수 있습니다.
리프 블록과, 그 뒤로 다음 리프 블록 또는 세그먼트의 끝까지 이어지는 모든 콘텐츠 블록은 하나의 파셀을 이룹니다. 비리프 블록 사이에 끼워 넣은 리프 블록이 없을 때, 파셀은 리프 블록, 그 리프 블록이 공식 블록인 경우 그것이 참조하는 비리프 블록, 그리고 경우에 따라 보조 블록으로 구성됩니다. 이 경우 압축 해제기는 파일의 파셀과 그 파일이 사용하는 보조 블록을 읽어 단일 파일을 읽을 수 있으며, 스트리밍 압축 해제기는 공식 블록을 그 파셀 너머까지 보관할 필요가 전혀 없습니다.
4.2.2. 등록 사전과 등록 모듈
등록 사전은 Aqueous 사전 레지스트리(§ 15.5 Aqueous 사전 레지스트리)의 엔트리이고, 등록 모듈은 Aqueous 모듈 레지스트리(§ 15.6 Aqueous 모듈 레지스트리)의 엔트리입니다. 각 엔트리는 그 바이트의 SHA-512/256 해시 [FIPS180-4]인 등록 해시로 식별되며, 그 바이트는 등록 사전의 경우 사전 블록의 디코딩된 데이터(§ 5.2 사전 블록)이고, 등록 모듈의 경우 wasm_module(§ 7.1 모듈 블록)입니다.
블록 5016(사전 블록)과 블록 6016(모듈 블록)은 약칭 블록입니다. 약칭 블록의 data는 체크섬이 있는 Zstandard 데이터 대신 32바이트 길이의 등록 해시만으로 이루어지며, 약칭 블록은 디코딩된 데이터가 해당 엔트리의 바이트인 같은 종류의 블록을 나타냅니다:
-
size는 32여야 합니다.
-
블록 5016의 등록 해시는 등록 사전의 것이어야 하며 이 등록 사전이 has_entropy_tables도 제공하고, 블록 6016의 등록 해시는 등록 모듈의 것이어야 합니다.
-
압축 해제기가 해당 레지스트리에서 등록 해시의 엔트리를 알지 못하면 미지원 실패가 발생합니다(§ 4.4 아카이브 해석).
-
약칭 블록은 그 밖에는 인덱싱, 블록 순서, 미러 블록 등에서 그것이 나타내는 사전 블록 또는 모듈 블록과 동일합니다.
두 레지스트리는 해시 함수를 공유하지만 엔트리는 공유하지 않으며, 약칭 블록은 자신과 같은 종류의 레지스트리에 대해서만 해석됩니다. 한 레지스트리의 엔트리를 안다고 해서 압축 해제기가 다른 레지스트리의 엔트리를 알게 되는 일은 결코 없습니다. 특히 블록 5016에 있는 등록 모듈의 해시는 모듈의 바이트로 이루어진 사전을 나타내지 않으며, 같은 바이트가 별도로 사전으로 등록된 경우에만 예외인데, 이때 둘은 서로 무관한 두 엔트리입니다.
약칭 블록에는 체크섬이 없으며, 등록 해시는 조회될 뿐 검증되지 않습니다. 따라서 해시의 손상은 한 번도 등록되지 않은 해시와 구별할 수 없으며, 두 경우 모두 잘못된 아카이브 실패가 아니라 미지원 실패가 됩니다.
4.3. 메타데이터 블록
블록 1016부터 1716까지는 메타데이터 블록이며, 그 유형은 0001 0mss(이진수)입니다(§ 3.1.1 블록 유형). 이 블록의 데이터는 공유 외부 구조(§ 3.2 공유 외부 구조)를 사용하며, checksum_method로는 XXH64를 사용하고 compression_method는 m에 따라 정해집니다. m이 0이면 None이고, m이 1이면 Zstandard입니다.
압축 해제 후의 내부 구조는 다음과 같습니다:
| 이름 | 타입 |
|---|---|
| previous_metadata_offset | leb128 |
| num_modules | leb128 |
| num_resources | leb128 |
| num_dictionaries | leb128 |
| num_directories | leb128 |
| num_files | leb128 |
| num_specials | leb128 |
| entries_size | leb128 = entries_end - entries_start |
| optional_metadata_size | leb128 |
| module_offsets | u(previous_metadata_offset)[num_modules] |
| resource_offsets | u(previous_metadata_offset)[num_resources] |
| dictionary_offsets | u(previous_metadata_offset)[num_dictionaries] |
| entry_parents | u(num_directories)[num_directories + num_files + num_specials - 1] |
| file_params | file_param[num_files] |
| entry_offsets | u(entries_size)[num_directories + num_files + num_specials - 1] |
| entries_start | offset |
| entries | 엔트리[num_directories + num_files + num_specials] |
| entries_end | offset |
| optional_metadata | u8[optional_metadata_size] |
모든 상대 오프셋은 현재 메타데이터 블록의 시작에서 거꾸로 측정됩니다. 상대 오프셋 o는 현재 메타데이터 블록보다 o 바이트 앞에서 시작하는 블록을 가리키며, 이는 오프셋 0이 유효하지 않음을 뜻합니다. 유효한 상대 오프셋은 모두 짝수이기도 합니다.
각 메타데이터 블록은 자신의 세그먼트에 있는 콘텐츠 블록, 즉 이전 메타데이터 블록이 있다면 그 뒤부터 현재 메타데이터 블록까지 나타나는 블록을 기술합니다. 이전 메타데이터 블록은 previous_metadata_offset을 통해 연결되며, 이 값은 해당 메타데이터 블록에 나타날 수 있는 최대 오프셋이기도 합니다. 이전 메타데이터 블록이 없으면 previous_metadata_offset은 암호화되지 않은 아카이브 구조(§ 3.5 암호화되지 않은 아카이브)에서 header_magic의 끝까지의 상대 오프셋에 1을 더한 값이며, 이로써 이 값은 결코 유효한 블록을 가리키지 않습니다.
num_modules, num_resources, num_dictionaries는 각각 세그먼트에 있는 모듈 블록, 리소스 블록, 사전 블록의 개수입니다. 이 블록들에 대한 상대 오프셋은 이후 나타나는 순서대로 각각 module_offsets, resource_offsets, dictionary_offsets로 기록됩니다.
메타데이터 블록은 0개 이상의 디렉터리, 0개 이상의 파일, 0개 이상의 특수 엔트리를 나타내며, 이들을 통틀어 엔트리라고 부르고 함께 번호를 매깁니다. 루트 디렉터리는 0번이고, 그 뒤의 디렉터리는 1번부터 num_directories번까지, 개별 파일은 (num_directories + 1)번부터 (num_directories + num_files)번까지, 특수 엔트리는 (num_directories + num_files + 1)번부터 (num_directories + num_files + num_specials)번까지 번호가 매겨집니다. 루트 디렉터리 외에 적어도 하나의 엔트리가 있어야 합니다. 즉 num_directories + num_files + num_specials ≥ 1이어야 합니다. entry_parents[k - 2]는 각 k ≥ 2에 대해 엔트리 k의 부모이며, 디렉터리가 순환을 이루지 않도록 k보다 작은 디렉터리, 즉 [0, min(k - 1, num_directories)] 범위에 속하는 디렉터리여야 합니다. 루트 디렉터리(엔트리 0)에는 부모가 없고 엔트리 1의 부모는 항상 루트 디렉터리이므로, 이들은 생략됩니다.
예를 들어 아카이브 전체의 선택적 메타데이터나 루트 디렉터리의 엔트리 추가 정보만 갱신하기 위해, 루트 디렉터리 외에 엔트리가 하나도 없는 세그먼트를 허용해야 하는지 여부입니다.
파일 매개변수(file_param)는 각 파일마다(디렉터리나 특수 엔트리에는 해당하지 않음) 나타나며, 다음으로 구성됩니다.
| 이름 | 타입 | 조건 |
|---|---|---|
| file_dictionary | u(num_dictionaries) | |
| file_offset | u(previous_metadata_offset) | |
| file_output | leb128 | If file_offset이 홀수 |
file_dictionary는 file_offset이 가리키는 블록이 사용하는 사전을 1부터 시작하는 인덱스로 나타낸 것이며, 사전을 사용하지 않으면 0입니다. 따라서 file_dictionary = k는 dictionary_offsets[k - 1]에 있는 블록을 가리키며, num_dictionaries는 이 값이 가질 수 있는 최댓값입니다. 파일의 파셀을 시작하는 해당 리프 블록까지의 상대 오프셋이 file_offset으로 기록되며, 파일이 블록의 출력 0이 아닌 다른 출력이면 여기에 1을 더합니다. 상대 오프셋은 0일 수 없습니다. 모든 상대 오프셋은 짝수이므로 file_offset % 2로 어느 경우인지 알 수 있습니다: file_offset이 짝수이면 파일 출력 인덱스는 0이고, 홀수이면 file_output + 1입니다. 여러 파일이 같은 리프 블록과 출력을 가리킬 수 있으며, 이 경우 그 파일들은 같은 내용을 가집니다.
그다음 엔트리 구조(엔트리)가 각 디렉터리, 파일, 특수 엔트리마다 이 순서대로 나타나며, 다음으로 구성됩니다:
| 이름 | 타입 | 조건 |
|---|---|---|
| entry_name | u8[*] ≠ 0 | |
| entry_name_sentinel | u8 = 0 | |
| original_size | leb128 | If 이 엔트리가 파일에 대한 것임 |
| special_kind | leb128 | If 이 엔트리가 특수 엔트리에 대한 것임 |
| link_target | u8[*] ≠ 0 | If 이 엔트리가 특수 엔트리에 대한 것이고 special_kind = 1임 |
| link_target_sentinel | u8 = 0 | If 이 엔트리가 특수 엔트리에 대한 것이고 special_kind = 1임 |
| device_major | leb128 | 이 엔트리가 특수 엔트리에 대한 것이고 special_kind가 3 또는 4인 경우 |
| device_minor | leb128 | 이 엔트리가 특수 엔트리에 대한 것이고 special_kind가 3 또는 4인 경우 |
| special_data | u8[*] | 이 엔트리가 특수 엔트리에 대한 것이고 special_kind가 4보다 큰 경우 |
각 엔트리의 이름은 entry_name에 따라 정해지며, entry_name은 § 4.1 아카이브 모델에 기술된 대로 엔트리 이름을 인코딩하고 그곳에서 디코딩됩니다. ZIP에서 온 엔트리 이름은 § 16.1 ZIP의 엔트리 이름에서 다룹니다.
특수 엔트리는 special_kind에 따라 결정되는, 파일이나 디렉터리가 아닌 노드를 나타냅니다:
| special_kind | 노드 |
|---|---|
| 0 | 선행 세그먼트 또는 베이스에서 같은 경로에 있는 노드를 제거하는 툼스톤 (§ 4.4.2 세그먼트 병합) |
| 1 | link_target을 대상으로 하는 링크 노드 |
| 2 | 종류가 "fifo"인 특수 노드
|
| 3 | 종류가 "char-device"이며 device_major와 device_minor를 장치 번호로 하는 특수 노드
|
| 4 | 종류가 "block-device"이며 device_major와 device_minor를 장치 번호로 하는 특수 노드
|
| 5 이상 | 이 명세의 향후 버전을 위해 예약된 미지 노드 |
link_target은 entry_name과 같은 인코딩을 사용합니다. special_data는 이 버전의 명세에서 해석되지 않으며, 그 길이는 entry_offsets에 있는 다음 엔트리의 시작 위치로 결정되고, 마지막 엔트리의 경우에는 entries_end로 결정됩니다.
미지 종류는 잘못된 아카이브 실패가 아니므로, 압축 해제기는 향후 정의될 종류를 사용하는 아카이브에서도 나머지 부분을 여전히 추출할 수 있습니다. 모든 특수 엔트리는 entry_offsets 덕분에 그 종류를 알지 못해도 건너뛸 수 있습니다. 미지 노드는 디렉터리 노드가 아닌 다른 모든 노드와 마찬가지로 같은 경로에 있는 앞선 노드를 대체하므로(§ 4.4.2 세그먼트 병합), 향후 정의될 종류가 세그먼트 병합 방식을 바꿀 수 없습니다. 이것이 툼스톤을 이 버전에서 첫 번째 종류로 정의하는 이유입니다. 툼스톤을 미지 노드로 취급하는 압축 해제기는 툼스톤이 제거하려던 노드 자리에 툼스톤을 그대로 남겨 두게 됩니다.
엔트리 배열은 바이트 수가 지정되지 않은 유일한 부분이며, 따라서 그 크기를 entries_size로 미리 알 수 있습니다. 그 밖의 모든 필드는 처음의 leb128 필드들을 읽은 뒤 임의 접근할 수 있습니다.
위의 상대 오프셋에 대한 예외로, entry_offsets는 entries_start로부터 측정합니다. 엔트리 1은 항상 entries_start에서 시작하므로 그 오프셋은 생략되며, 각 k ≥ 2에 대해 entry_offsets[k - 2]는 엔트리 k의 시작 위치까지의 오프셋입니다. 이 값들은 엔트리의 실제 위치와 일치해야 합니다.
optional_metadata는 선택 메타데이터(§ 11 선택적 메타데이터)에서 설명합니다.
4.4. 아카이브 해석
바이트열 input, 선택적인 패스프레이즈 passphrase, 그리고 아카이브 값인 선택적인 베이스 base가 주어졌을 때 아카이브를 해석하려면 다음과 같이 합니다.
-
input이 ENCRYPTED_HEADER_MAGIC 또는 ENCRYPTED_MIRRORED_HEADER_MAGIC으로 시작하면 다음과 같이 합니다.
-
input 전체를 암호화 아카이브 구조(§ 9 암호화 아카이브)로 파싱합니다. 파싱에 실패하면 잘못된 아카이브 실패를 반환합니다.
-
header_checksum이 있고 일치하지 않으면 잘못된 아카이브 실패를 반환합니다.
-
lg_work_factor가 구현에서 정의한 한도를 초과하면 리소스 실패를 반환합니다.
-
패스프레이즈 passphrase가 주어지지 않았거나, [AGE]에 설명된 대로 passphrase로 encrypted_file_key에서 파일 키를 언래핑할 수 없으면, 복호화 실패를 반환하십시오.
-
풀어낸 파일 키를 사용하여, [AGE]에 설명된 대로 재구성한 age 헤더에 대해 age_header_mac을 검증하고 age 페이로드를 복호화합니다. 둘 중 하나라도 실패하면 잘못된 아카이브 실패를 반환합니다.
-
input을 복호화된 페이로드로 설정합니다. 이 페이로드는 § 9 암호화 아카이브의 추가 제약 조건을 충족해야 하며, 그렇지 않으면 잘못된 아카이브 실패를 반환합니다.
-
-
모든 블록의 data를 파싱하지 않고 바이트열로 취급하여 input 전체가 암호화되지 않은 아카이브 구조(§ 3.5 암호화되지 않은 아카이브)로 파싱되고, 블록 중 어느 하나의 타입이 필수이면서 이 명세에서 정의하지 않은 것이거나, 블록 중 어느 하나가 data가 32바이트이고 압축 해제기가 그 등록 해시를 알지 못하는 약칭 블록이면(§ 4.2.2 등록 사전과 등록 모듈), 미지원 실패를 반환합니다.
-
input 전체를 암호화되지 않은 아카이브 구조(§ 3.5 암호화되지 않은 아카이브)로 파싱합니다. 파싱에 실패하면 잘못된 아카이브 실패를 반환합니다.
-
input이 독립 아카이브이면, 베이스 base를 root가 children으로 빈 맵을, extra로 null을 가지는 디렉터리 노드이고 extra가 빈 맵인 아카이브 값으로 설정하십시오.
-
베이스 base의 root가 디렉터리 노드이면, root를 그 복사본으로 두십시오. 그렇지 않으면 root를 빈 맵을 children으로, null을 extra로 갖는 디렉터리 노드로 둡니다.
-
extra를 베이스 base의 extra의 사본으로 둡니다.
아카이브 베이스는 5단계 이후의 base이며, 세그먼트의 선행 결과는 9단계에서 해당 세그먼트가 해석되기 직전의 root이거나, 메타데이터 블록이 없는 아카이브의 경우 아카이브 베이스의 root입니다. 이들은 각각 외부 임포트와 내부 임포트가 참조하는 대상입니다(§ 8.2 임포트 해석). 주어진 베이스 base는 독립 아카이브의 해석에 영향을 주지 않습니다.
이 알고리즘의 입력은 정확히 header_magic의 첫 번째 바이트부터 footer_size_copy의 마지막 바이트까지의 아카이브입니다.
파일은 앞쪽 또는 뒤쪽 바이트가 있는 아카이브를 포함할 수 있으며, 압축 해제기는 아카이브 위치 찾기(§ 3.6 아카이브 찾기)를 통해 이를 제거합니다.
모든 콘텐츠 블록은 뒤따르는 메타데이터 블록에 의해 기술되어야 하므로(§ 4.2 콘텐츠 블록), 메타데이터 블록이 있는 아카이브는 마지막 메타데이터 블록 뒤에 어떤 콘텐츠 블록도 가질 수 없습니다. 압축 해제기가 블록을 앞쪽으로 스캔하거나 오프셋을 뒤쪽으로 따라가는 방식 중 어느 쪽으로든 각 세그먼트를 찾을 수 있도록, previous_metadata_offset와 metadata_backward_offset는 § 4.3 메타데이터 블록 및 § 3.5 암호화되지 않은 아카이브에 기술된 대로 blocks를 파싱하여 찾은 메타데이터 블록을 가리켜야 합니다.
4.4.1. 세그먼트 해석하기
메타데이터 블록이 metadata block인 segment를 세그먼트 해석하려면 다음을 수행합니다:
-
meta를 메타데이터 블록 metadata block의 디코딩된 데이터를 그 내부 구조(§ 4.3 메타데이터 블록)로 파싱한 것으로 둡니다.
-
d를 meta의 num_directories, f를 num_directories + num_files, n을 num_directories + num_files + num_specials라고 합니다.
-
nodes를 빈 맵을 children으로, 엔트리 0에 대한 엔트리 extra를 extra로 하는 단일 디렉터리 노드를 포함하는 리스트라고 합니다.
-
[1, n]의 각 k에 대해 다음을 수행합니다:
-
entry를 엔트리 meta.entries[k - 1]로 둡니다.
-
name을 엔트리 entry.entry_name을 디코딩한 결과로 둡니다.
-
parent를 k = 1이면 0, 그렇지 않으면 meta.entry_parents[k - 2]라고 합니다.
-
그렇지 않고 k ≤ f이면 다음을 수행합니다:
-
param을 meta.file_params[k - d - 1]이라고 합니다.
-
block을 메타데이터 블록 metadata block으로부터 상대 오프셋 param.file_offset - param.file_offset % 2에 있는 블록으로 둡니다. 이 블록은 세그먼트 segment 안의 리프 블록이어야 합니다.
-
param.file_dictionary가 0이면 블록 block은 사전을 사용해서는 안 됩니다. 그렇지 않으면, block은 meta.dictionary_offsets[param.file_dictionary - 1]에 있는 사전 블록을 사용해야 합니다.
-
param의 파일 출력 인덱스에 대한 블록 block의 파일 콘텐츠를 content라 합니다. 그 길이는 엔트리 entry.original_size여야 합니다.
-
엔트리 k에 대한 엔트리 extra에 값 g를 가진
link-group이 있으면, g ≥ 0인 경우 node.링크 그룹을 segment와 g의 쌍으로, 그렇지 않은 경우 g로 설정하십시오.
-
-
그렇지 않고 entry.special_kind가 0이면, node를 툼스톤으로 하십시오. 엔트리 k에 대한 엔트리 추가 정보는 null이어야 합니다.
-
그렇지 않고 엔트리 entry.special_kind가 1이면:
-
엔트리 entry.link_target을 디코딩한 결과를 target이라 합니다.
-
-
그렇지 않고 엔트리 entry.special_kind가 2이면, node를 종류가 "
fifo"이고 장치가 null인 특수 노드라 합니다. -
그렇지 않고 엔트리 entry.special_kind가 3 또는 4이면, node를 종류가 각각 "
char-device" 또는 "block-device"이고 장치가 (entry.device_major, entry.device_minor)인 특수 노드라 합니다. -
그렇지 않으면, node를 종류가 엔트리 entry.special_kind이고 데이터가 entry.special_data인 미지 노드라 합니다.
-
children을 nodes[parent]의 자식으로 하십시오. children[name]은 존재해서는 안 됩니다.
-
children[name]을 node로 설정하십시오.
-
node를 nodes에 추가하십시오.
-
-
nodes[0]을 반환하십시오.
§ 4.3 메타데이터 블록에 따라 모든 parent는 k보다 작고 d 이하이므로, nodes[parent]는 항상 nodes에 이미 있는 디렉터리 노드입니다.
엔트리 k에 대한 엔트리 추가 정보는 세그먼트의 선택적 메타데이터에 있는 entry-extras의 0부터 시작하는 인덱스 k에 있는 요소이며, 인덱스 0은 루트 디렉터리에 해당합니다. entry-extras 또는 해당 요소가 존재하지 않으면 null입니다. entry-extras는 루트 디렉터리를 포함한 엔트리 수, 즉 num_directories + num_files + num_specials + 1보다 많은 요소를 가져서는 안 됩니다.
4.4.2. 세그먼트 병합
later를 earlier에 디렉터리 노드 병합하려면 다음을 수행합니다.
-
later.children의 각 name → 노드 node에 대해 다음을 수행합니다.
결과적으로 여러 세그먼트에 나타나는 디렉터리는 그 자식들의 합집합을 가지며, 그 밖의 노드나 다른 종류의 노드로 대체된 디렉터리는 그 자손과 함께 통째로 대체됩니다. 특히 링크 노드로 대체된 디렉터리는 모든 자손을 잃으므로, 결과에서 어떤 노드도 링크 노드의 자손이 아닙니다. 툼스톤은 자신의 경로에 있는 노드를 그 자손과 함께 제거하며, 그러한 노드가 없으면 아무것도 하지 않습니다. 툼스톤은 이전에 존재하지 않던 디렉터리 아래에 있더라도 결과에 결코 남지 않습니다. 이후 세그먼트에서 extra가 null인 디렉터리는 기존 extra를 유지하며, 대신 빈 맵 {}으로 대체할 수 있습니다.
5. 저장된 데이터
5.1. 데이터 블록
블록 2016부터 3716까지와 4016부터 4B16까지는 데이터 블록입니다. 데이터 블록은 compression_method가 None이 아니면 압축된 블록이고, 그렇지 않으면 압축되지 않은 블록입니다.
데이터 블록의 type은 001kk css 리프 블록의 경우 이진수로 (§ 3.1.1 블록 유형) 이와 같고,0100 kkss 비리프 블록의 경우 다음과 같으며, 여기서:
-
kk는 compression_method와 사전 사용 여부(has_dictionary)를 결정합니다: 0은 사전 없는 None, 1은 사전 없는 Zstandard, 2는 사전을 사용하는 Zstandard입니다. 3은 향후 압축 방식을 위해 예약되어 있습니다.
-
c는 checksum_method를 결정합니다: 0이면 None이고, 1이면 XXH64입니다. 비리프 데이터 블록은 항상 XXH64를 사용합니다(§ 10.1 체크섬).
그 데이터는 다음으로 구성됩니다:
| 이름 | 타입 | 조건 |
|---|---|---|
| dictionary_index | leb128 | has_dictionary인 경우 |
| compressed_data | compressed_data | compression_method가 None이 아닌 경우 |
| uncompressed_data | uncompressed_data | compression_method가 None인 경우 |
사전을 사용하는 경우, 그 0부터 시작하는 인덱스는 dictionary_index로 지정됩니다. 리프 데이터 블록의 경우 메타데이터 블록도 사전을 file_dictionary로 제공하며, 이는 블록을 읽기 전에 사용할 수 있습니다. 비리프 데이터 블록의 경우 dictionary_index가 사전에 대한 유일한 참조입니다. compressed_data와 uncompressed_data는 위와 같이 결정된 compression_method 및 checksum_method와 함께 공유 외부 구조(§ 3.2 공유 외부 구조)를 사용합니다. uncompressed_data는 해석되지 않은 원시 바이트열을 나타냅니다.
압축된 블록은 하나의 압축 해제 단위입니다. 즉, 그 내용의 어느 부분을 읽든 처음부터 압축을 해제해야 합니다. 원하는 임의 접근 단위보다 큰 내용은 여러 블록으로 분할됩니다.
-
일반적으로 파일은 여러 비리프 블록을 연결하는 공식 블록으로 표현되며, 이 비리프 블록들은 보통 함께 하나의 파셀을 이룹니다. 그러면 공식 블록은 파셀 내 임의 접근을 위한 인덱스 역할을 합니다.
-
메타데이터 블록이 없는 아카이브(§ 3.5 암호화되지 않은 아카이브)에서는 파일이 단순히 여러 리프 블록으로 표현됩니다.
5.2. 사전 블록
블록 5016부터 5716까지는 사전 블록이며, 그 type은 이진수로 0101 0tss입니다(§ 3.1.1 블록 유형). 그 데이터는 체크섬이 있는 Zstandard 데이터(§ 3.2 공유 외부 구조)이지만, 블록 5016은 등록 사전에 대한 약칭 블록(§ 4.2.2 등록 사전과 등록 모듈)이라는 점이 예외입니다.
t는 사전이 엔트로피 테이블을 가지는지 여부(has_entropy_tables)를 결정합니다. 0이면 false이고, 1이면 true입니다.
압축 해제 후의 내부 구조는 다음과 같습니다.
| 이름 | 타입 | 조건 |
|---|---|---|
| entropy_tables | u8[*] | has_entropy_tables인 경우 |
| repeat_offsets | u32le[3] | has_entropy_tables인 경우 |
| content | u8[*] |
사전 블록의 디코딩된 데이터는 그것을 사용하는 블록들을 위한 사전이며, Magic_Number와 Dictionary_ID가 없는 Zstandard 사전([RFC8878], 5절)에 해당합니다.
-
has_entropy_tables가 true이면, entropy_tables는
Entropy_Tables구조이며 그 길이는 이를 파싱하여 결정되고, repeat_offsets는 순서대로 세 개의Repeat_Offsets입니다. 각 반복 오프셋은 [1, content의 길이] 범위 안에 있어야 합니다. -
그렇지 않으면 원시 내용 사전입니다. 즉, content가 사전
Magic_Number로 시작하더라도 엔트로피 테이블은 빈 상태로 시작하고 반복 오프셋은 초깃값(1, 4, 8)으로 시작합니다.
두 경우 모두 content는 사전의 Content이며, 이는 시퀀스가 선행 데이터로 참조할 수 있는 사전의 유일한 부분입니다. content는 길이가 최소 8바이트여야 합니다.
Magic_Number와 Dictionary_ID는 Zstandard 프레임에서만 의미가 있고 프레이밍은 이 형식이 수행하므로 생략됩니다. libzstd를 사용하는 구현은 엔트로피 테이블이 있는 사전을 로드하기 위해 (임의의 0이 아닌 Dictionary_ID와 함께) 이들을 앞에 덧붙일 수 있습니다. 그 경우 구현은 어떤 시퀀스도 앞에 덧붙인 바이트나 entropy_tables를 참조하지 않도록 보장해야 하는데, libzstd의 일부 압축 해제 경로는 이를 검사하지 않기 때문입니다.
ZDICT_finalizeDictionary) 주로 작은 블록에 이점이 있습니다. 1–20 KB 파일의 파일별 블록으로 측정한 결과 크기가 0.4–26% 줄었지만, 64 KB 이상의 블록에서는 이점이 사라졌습니다. libzstd의 빠른 전략(예: 1.5.7의 레벨 1–3)은 블록 자체의 테이블이 더 작은 경우에도 모든 블록에 사전의 엔트로피 테이블을 재사용합니다. 사전의 반복 오프셋이 초기값인 경우, 압축기는 같은 content를 원시 내용 사전으로 사용하여 블록을 한 번 더 압축하고 더 작은 결과를 선택함으로써 이를 피할 수 있습니다. 그렇게 압축된 블록은 사전의 엔트로피 테이블을 결코 참조하지 않기 때문입니다.
5.3. 블록 내용
공유 외부 구조(§ 3.2 공유 외부 구조)를 사용하는 블록의 디코딩된 데이터는 그 compression_method와 checksum_method가 주어졌을 때 다음과 같이 결정됩니다.
-
compression_method가 None이면 data입니다.
-
compression_method가 Zstandard이면, 거기에 설명된 대로 compression_params와, 사용 중인 사전 블록(§ 5.2 사전 블록)이 있는 경우 그 사전 블록이 제공하는 사전을 사용하여 data를 압축 해제한 결과입니다. 결과는 정확히 original_size 바이트여야 합니다.
-
checksum_method가 None이 아니면, 체크섬은 checksum_method로 계산한 디코딩된 데이터의 체크섬과 같아야 합니다.
데이터 블록의 저장된 내용은 리프 블록인지 여부와 관계없이 그 compressed_data 또는 uncompressed_data의 디코딩된 데이터입니다. 비리프 항목의 저장된 내용은 그것을 제공하는 비리프 데이터 블록의 저장된 내용이거나, 그것을 제공하는 임포트의 임포트된 내용입니다(§ 8.2 임포트 해석). 사전을 사용하는 데이터 블록은 세그먼트 내 dictionary_index 위치의 사전 블록을 사용합니다.
출력 인덱스 i에 대한 리프 블록의 파일 내용은 다음과 같습니다.
-
데이터 블록이면 그 저장된 내용입니다. i는 0이어야 합니다.
-
공식 블록이면 그것을 평가한 결과의 인덱스 i에 있는 출력입니다. i는 그 num_outputs보다 작아야 합니다.
6. 공식
6.1. 공식 블록
블록 7016부터 7716까지는 공식 블록이며, 그 type은 0111 0dss 이진수로 다음과 같습니다(§ 3.1.1 블록 유형).
d는 사전 사용 여부(has_dictionary)를 결정합니다. 0이면 거짓이고, 1이면 참입니다.
그 데이터는 다음으로 구성됩니다.
| 이름 | 타입 | 조건 |
|---|---|---|
| dictionary_index | leb128 | has_dictionary인 경우 |
| num_outputs | leb128 | |
| formula_data | compressed_data |
사전을 사용하는 경우, 데이터 블록에서와 마찬가지로 그 0부터 시작하는 인덱스를 dictionary_index로 지정합니다.
num_outputs는 공식 블록의 출력 개수이며, 1 이상이어야 합니다(§ 6.3 공식 평가).
공식 블록은 파일의 내용을, 그 뒤에 오는 비리프 항목에 대한 식으로 기술하며, 이 식은 그 앞에 오는 모듈과 리소스로 평가됩니다. formula_data의 내부 구조는 공식 데이터(§ 6.2 공식 데이터)에서 설명합니다.
6.2. 공식 데이터
이 절에서는 공식 블록(§ 6.1 공식 블록)에 있는 formula_data의 압축을 해제한 후의 내부 구조를 설명합니다.
공식은 그것이 참조하는 블록의 목록들과 그 뒤에 오는 스택 기계의 명령으로 구성됩니다. 각 명령은 스택에서 0개 이상의 바이트열을 꺼내고 0개 이상의 바이트열을 넣으며, 마지막 명령 이후 스택에 남아 있는 바이트열이 공식의 출력입니다.
| 이름 | 타입 |
|---|---|
| num_blocks | leb128 |
| block_ordinal_deltas | leb128[num_blocks] |
| num_resources | leb128 |
| resource_indices | leb128[num_resources] |
| num_modules | leb128 |
| module_indices | leb128[num_modules] |
| instructions | 명령[*] |
block_ordinal_deltas는 공식 블록이 참조하는 비리프 항목을 나열합니다. 이름과 달리 num_blocks, block_ordinal_deltas, slice_block은 비리프 항목을 세며, 비리프 항목은 임포트를 제외하면 블록입니다. 세그먼트 안에서 공식 블록 뒤에 오는 비리프 블록이 제공하는 비리프 항목 사이에서 나타나는 순서에 따른 이들의 0부터 시작하는 서수는 o0 = block_ordinal_deltas[0]이고, i ≥ 1에 대해 oi = oi - 1 + block_ordinal_deltas[i] + 1이므로, 서수는 순증가합니다. i번째 참조된 항목은 서수가 oi인 비리프 항목이며, 이 항목은 존재해야 합니다.
resource_indices와 module_indices는 공식 블록이 참조하는 리소스 블록과 모듈 블록을 세그먼트 안에서의 0부터 시작하는 인덱스로 나열합니다(§ 4.2 콘텐츠 블록). 각 인덱스는 존재하는 블록을 가리켜야 하며, 그 블록은 § 4.2.1 블록 순서에 따라 공식 블록보다 앞에 옵니다.
명령이 formula_data의 나머지를 채웁니다. 위 목록에 나열된 각 항목 또는 블록은 적어도 하나의 명령에서 사용되어야 하며, 그 경우에만 공식 블록이 참조한다고 합니다.
각 명령은 u8 타입의 옵코드와 그 뒤에 오는 피연산자로 구성되며, 피연산자의 구조는 다음 절에서 설명하는 바와 같이 옵코드에 따라 결정됩니다. opcode는 다음 중 하나여야 합니다.
| 옵코드 | 명령 | 팝 | 푸시 |
|---|---|---|---|
| 0016 | Nop | 0 | 0 |
| 0116 | Literal | 0 | 1 |
| 0F16 | Swap | 0 | 0 |
| 1016 | Slice | 0 | 1 |
| 1116 | Apply | apply_num_inputs | apply_num_outputs |
| 1216 | Concatenate | concat_count | 1 |
| 1316 | Pack | pack_count | 1 |
| 1416 | Split | 1 | split_count |
| 1516 | Patch | 2 | 1 |
| 2016 | 체크섬 | 0 | 0 |
옵코드는 역할에 따라 분류됩니다. 0016부터 0F16까지는 공식 자체에 포함된 데이터를 푸시하거나 스택을 재배열하고, 1016부터 1F16까지는 참조 항목이나 스택으로부터 바이트열을 계산하며, 2016부터 2F16까지는 이를 검사합니다. 그 밖의 옵코드는 예약되어 있습니다.
같은 세그먼트 안의 앞선 블록에 대한 참조(예: 세그먼트 앞부분에 저장된 파일을 패치하기 위한 참조)는 아직 정의되지 않았습니다. 다른 파일에 대한 참조는 명령이 아니라 임포트(§ 8.1 임포트 블록)로 이루어지며, 선행 세그먼트에 있는 파일의 내용에 대한 참조는 내부 임포트로 이루어집니다. 후보안은 공식 블록 앞에 놓이는 두 번째 비리프 항목 목록으로, Slice 명령이 참조 항목과 같은 방식으로 읽으며, 다른 공식 블록의 출력이 필요할 때는 그 출력을 참조하는 대신 해당 공식 블록의 명령을 복사합니다. 이를 위해서는 스트리밍 압축 해제기가 어떤 블록을 유지해야 하는지 알 수 있도록 참조되는 블록보다 앞서 선언되는 이러한 참조 거리의 상한과, 세그먼트를 처음부터 읽지 않고도 앞선 블록의 위치를 찾는 방법이 필요합니다(docs/xform-design.md §7).
formula_data는 임의 접근이 불가능하며, 압축 해제기는 공식의 어떤 부분이든 평가하기 전에 이를 전부 읽고 압축 해제할 것으로 예상됩니다. 목록에 대한 인덱스나 스택 위 바이트열의 개수처럼 평가된 바이트열에 의존하지 않는 formula_data에 대한 모든 요구 사항은 아무것도 평가하지 않고 명령을 한 번 훑는 것만으로 검사할 수 있습니다. 같은 훑기 과정에서 스택 위 모든 바이트열의 길이가 결정되므로, formula_data는 다른 형식의 탐색 테이블과 같은 방식으로 출력 내 임의 접근을 위한 인덱스 역할을 합니다. 참조 항목은 공식 블록 뒤에 오는 블록을 각각의 size 필드에 따라 건너뛰어 위치를 찾으며, 임포트 블록의 항목은 해당 블록을 디코딩하여 위치를 찾습니다.
마지막 항목이 맨 위인 바이트열의 리스트인 스택에서 명령을 실행하려면, 다음 절들에서 해당 옵코드에 대해 설명하는 단계를 수행하십시오. n개의 항목을 팝하려면 스택에 적어도 n개의 항목이 있어야 하며, 마지막 n개의 항목이 스택에서 제거되어 스택에 있던 순서대로 사용됩니다.
6.2.1. Nop 명령
옵코드 0016은 Nop 명령을 나타내며, 이 명령은 피연산자가 없고 아무 동작도 하지 않습니다.
6.2.2. Literal 명령
옵코드 0116은 Literal 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다.
| 이름 | 타입 |
|---|---|
| literal_size | leb128 |
| literal_data | u8[literal_size] |
literal_data를 푸시합니다.
6.2.3. Swap 명령
옵코드 0F16는 Swap 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다.
| 이름 | 타입 |
|---|---|
| swap_depth | leb128 |
스택에는 swap_depth개보다 많은 항목이 있어야 합니다. 이 명령은 스택의 맨 위 항목을 그보다 swap_depth개 아래에 있는 항목과 교환합니다.
6.2.4. Slice 명령
옵코드 1016은 Slice 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다.
| 이름 | 타입 |
|---|---|
| slice_block | leb128 |
| slice_start | leb128 |
| slice_length | leb128 |
-
slice_block은 num_blocks보다 작아야 합니다.
-
c를 slice_block번째 참조 항목의 저장된 내용이라고 합니다.
-
slice_start + slice_length는 c의 길이 이하여야 합니다.
-
c에서 [slice_start, slice_start + slice_length) 범위의 바이트를 푸시합니다.
6.2.5. Apply 명령
옵코드 1116은 Apply 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다.
| 이름 | 타입 |
|---|---|
| apply_module | leb128 |
| apply_num_resources | leb128 |
| apply_resources | leb128[apply_num_resources] |
| apply_params_size | leb128 |
| apply_params | u8[apply_params_size] |
| apply_memory_limit | leb128 |
| apply_num_inputs | leb128 |
| apply_num_outputs | leb128 |
| apply_output_sizes | leb128[apply_num_outputs] |
-
apply_module는 num_modules보다 작아야 하며, apply_resources의 각 항목은 num_resources보다 작아야 합니다.
-
apply_num_outputs는 1 이상이어야 합니다.
-
apply_num_inputs개의 항목을 꺼내 inputs로 둡니다.
-
outputs를 module_indices[apply_module]에 있는 모듈 블록을 inputs, apply_resources의 각 r에 대해 resource_indices[r]에 있는 리소스 블록, apply_params, apply_output_sizes, apply_memory_limit와 함께 적용한 결과로 둡니다(§ 7.3 모듈 실행).
-
outputs의 각 항목을 순서대로 넣습니다.
6.2.6. Concatenate 명령
옵코드 1216은 Concatenate 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다.
| 이름 | 타입 |
|---|---|
| concat_count | leb128 |
이 명령은 concat_count개의 항목을 꺼내고, 이들을 순서대로 연결한 결과를 넣습니다.
6.2.7. Pack 명령
옵코드 1316은 Pack 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다.
| 이름 | 타입 |
|---|---|
| pack_count | leb128 |
pack_count개의 항목을 꺼내고 그것들의 패킹된 바이트열을 순서대로 넣습니다(§ 6.3.1 패킹된 바이트열).
6.2.8. Split 명령
옵코드 1416은 Split 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다:
| 이름 | 타입 |
|---|---|
| split_count | leb128 |
| split_sizes | leb128[split_count] |
-
split_count는 1 이상이어야 합니다.
-
항목 x를 꺼냅니다. split_sizes의 합은 x의 길이와 같아야 합니다.
-
split_sizes의 각 n에 대해 순서대로 x의 다음 n바이트를 넣습니다.
6.2.9. Patch 명령
옵코드 1516는 Patch 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다:
| 이름 | 타입 |
|---|---|
| patch_size | leb128 |
-
항목 2개를 source와 patch로 꺼냅니다.
-
patch는 다른 바이트 없이 하나 이상의 Zstandard 블록으로 구성되어야 하며, 마지막 블록만 블록 헤더에서 최종 블록으로 표시되고, 각 블록은 최대 128 KiB의 데이터를 담습니다.
-
output을 source를 원시 내용 사전의
Content로 하여 patch를 Zstandard 블록 [RFC8878]의 시퀀스로 압축 해제한 결과라고 합니다. 즉, 엔트로피 테이블은 빈 상태로 시작하고 반복 오프셋은 초깃값(1, 4, 8)으로 시작합니다. 모든 매치의 오프셋은 source의 길이와 이미 압축 해제된 바이트 수를 더한 값 이하여야 합니다. -
output의 길이는 patch_size여야 합니다.
-
output을 넣습니다.
zstd --patch-from과 마찬가지로 이전 바이트열을 원시 내용 사전으로 하여 Zstandard 블록을 압축 해제하므로, 압축 해제기는 이를 위해 자신의 Zstandard 압축 해제기를 재사용합니다. 앞선 바이트열은 사전 블록이 아니라 스택 위의 피연산자이므로, 공식이 계산한 임의의 바이트열, 예를 들어 임포트나 모듈이 그로부터 분할한 스트림이 될 수 있습니다. 패치 자체는 일반적으로 압축되지 않은 비리프 데이터 블록의 슬라이스이며, 그 체크섬은 저장된 패치를 대상으로 하고, 출력 체크섬은 결과를 대상으로 합니다.
Window_Size도 없습니다. 매치는 source의 어떤 바이트나 지금까지의 출력의 어떤 바이트든 참조할 수 있으며, § 3.2.1 Zstandard 압축 방식에서 보장하는 Window_Size의 한도는 적용되지 않습니다. 압축 해제기는 어차피 두 바이트열을 모두 보유하고 있으므로, 먼 거리를 참조하더라도 추가 메모리가 들지 않습니다. libzstd에서는 이것이 source로 ZSTD_DCtx_refPrefix를 호출한 뒤 프레임을 압축 해제하는 것에 해당하며, 그 릴리스 빌드는 오프셋을 프리픽스의 시작 위치에 대해서만 검사합니다. Window_Size를 강제하는 압축 해제기는 해당 프레임에 대해 그 검사를 해제해야 합니다. libzstd를 사용하는 압축기는 zstd --patch-from과 마찬가지로 ZSTD_c_windowLog를 source와 출력을 포괄할 만큼 크게 설정합니다.
6.2.10. Checksum 명령
옵코드 2016은 Checksum 명령을 나타냅니다. 피연산자는 다음과 같이 구성됩니다.
| 이름 | 타입 |
|---|---|
| checksum_value | u64le |
스택은 비어 있어서는 안 됩니다. 스택 최상단의 XXH64 체크섬은 checksum_value와 같아야 합니다.
Checksum 명령은 실행될 때 스택 최상단에 있는 바이트열을 포괄하며, 그 바이트열은 Swap 명령으로 이동되어도 같은 항목으로 유지됩니다. 출력 체크섬은 공식 블록의 출력을 포괄하는 Checksum 명령입니다.
6.3. 공식 평가
공식 블록 block을 평가하려면 다음과 같이 하십시오.
-
블록 block의 formula_data의 디코딩된 데이터를 § 6.2 공식 데이터에서와 같이 파싱한 것을 data로 둡니다.
-
data의 명령의 각 insn에 대해 순서대로, § 6.2 공식 데이터에서 해당 옵코드에 대해 기술된 대로 스택 stack에서 insn을 실행합니다.
-
스택 stack의 항목 수는 블록 block의 num_outputs와 같아야 합니다.
-
stack을 블록 block의 출력으로 반환하며, 출력에는 스택의 맨 아래부터 순서대로 0부터 번호를 매깁니다.
6.3.1. 패킹된 바이트열
바이트열의 리스트 items의 패킹된 바이트열은 다음을 연결한 것이며, 여기서 n은 items의 개수입니다.
-
n을 u64le로 나타낸 것.
-
[0, n)의 각 i에 대해, items[0]부터 items[i]까지의 길이의 합을 u64le로 나타낸 것.
-
items의 각 항목을 순서대로 나열한 것.
바이트열 p는, 그러한 것이 있다면, 정확히 하나의 리스트 items의 패킹된 바이트열입니다. p를 언패킹하려면 다음과 같이 합니다.
-
p가 8바이트보다 짧으면 실패를 반환합니다.
-
n을 p의 처음 8바이트를 u64le로 읽은 값으로 하고, h를 8 × (n + 1)로 합니다. h가 p의 길이를 초과하면 실패를 반환합니다.
-
[0, n)의 각 i에 대해 ei를 오프셋 8 × (i + 1)부터 시작하는 p의 8 바이트를 u64le로 읽은 값이라고 하고, e-1을 0이라고 합니다.
-
[0, n)의 어떤 i에 대해서든 ei - 1 > ei이면 실패를 반환합니다.
-
h + en - 1이 p의 길이가 아니면 실패를 반환합니다.
-
[0, n)의 각 i에 대해 [h + ei - 1, h + ei) 범위에 있는 p의 바이트들로 이루어진 리스트를 반환합니다.
반대 방향으로, 바이트열의 리스트를 생성하는 모듈은 그것들을 단순히 연결한 결과를 출력하며, Split 명령이 이를 공식에 선언된 크기에 따라 분리합니다. 그러한 모듈의 출력은 Split 명령 뒤에 Pack 명령을 두어 패킹된 바이트열을 받는 다른 모듈로 전달됩니다.
7. 모듈
7.1. 모듈 블록
블록 6016부터 6316까지는 모듈 블록이며, 그 유형은 0110 00ss입니다(§ 3.1.1 블록 유형). 이들의 데이터는 체크섬이 있는 Zstandard 데이터입니다(§ 3.2 공유 외부 구조). 단, 블록 6016은 등록 모듈에 대한 약칭 블록입니다(§ 4.2.2 등록 사전과 등록 모듈).
압축 해제 후의 내부 구조는 다음과 같습니다.
| 이름 | 타입 |
|---|---|
| wasm_module | u8[*] |
wasm_module은 바이너리 형식 [WASM-CORE-3]의 WebAssembly 모듈, 즉 일반적인 .wasm 파일의 내용으로, 매직 00 61 73 6D와 버전 01 00 00 00(16진수)으로 시작하며 디코딩된 데이터 전체에 걸쳐 있습니다. 이 모듈은 § 7.3 모듈 실행에서 허용하는 기능으로 이루어진 모듈로서 디코딩 및 검증되어야 하며, 그곳에서 정한 프로필과 인터페이스를 준수해야 합니다. 그렇지 않으면 잘못된 아카이브 실패가 발생합니다. 요컨대, 모듈은 자체 메모리를 정의하지 않고 모든 메모리와 호스트 함수를 모듈 이름 aqr에서 임포트하며, 시작 함수가 없고, 호스트가 적용할 때마다 한 번씩 호출하는 단일 엔트리 포인트를 익스포트합니다. 사용자 정의 섹션은 허용되며 결과에 영향을 주지 않습니다. 이 형식은 모듈 주위에 아무것도 덧붙이지 않으므로, 디코딩된 데이터는 어떤 WebAssembly 도구로도 검사하고 실행할 수 있으며, 등록 모듈에 대한 모듈 블록은 해당 엔트리와 같은 wasm_module을 가집니다.
모듈 블록의 모듈 해시는 그 wasm_module의 SHA-512/256 해시 [FIPS180-4]이며, 이는 등록 모듈의 등록 해시이기도 합니다(§ 4.2.2 등록 사전과 등록 모듈). 모듈의 모든 적용은 wasm_module과 그 인수에만 의존하므로(§ 7.3 모듈 실행), 압축 해제기는 모듈 해시를 알고 있는 모듈을 wasm_module을 인스턴스화하는 대신 자체 구현(예: 네이티브 코드)으로 적용할 수 있습니다. 단, 그 결과는 잘못된 아카이브 실패를 포함하여 정의와 구별할 수 없어야 합니다. 그러한 압축 해제기는 모듈이 약칭 블록으로 주어지든 전체로 주어지든 모듈의 바이트도 WebAssembly 엔진도 필요로 하지 않습니다. 압축 해제기는 자신의 적합성 수준이 허용하는 바에 따라 모듈의 적용을 거부할 수 있으며(§ 13.1 적합성 수준), 이 경우 리소스 실패가 발생합니다.
-
자체 구현을 적용합니다. 이는 가장 빠르지만 알고 있는 모든 모듈마다 만들어야 합니다. 결과가 실패를 포함하여 모듈과 구별할 수 없어야 하므로, 새로 작성하기보다는 자동화된 수단(예: wasm2c)으로 모듈을 변환하는 편이 바람직합니다. 또는
-
해시가 충분히 강력하여 다른 어떤 모듈도 같은 해시를 가질 수 없으므로, 격리하지 않고 WebAssembly 엔진으로 모듈을 인스턴스화합니다. 이 방식은 전체로 주어진 모듈에 대해서는 해시만 필요로 하며, 바이트는 약칭 블록에 대해서만 필요로 합니다.
모듈 해시를 알지 못하는 모듈에 대해서는 다음 중 하나를 할 수 있습니다.
-
신뢰할 수 없는 코드로서 샌드박스 안에서 WebAssembly 엔진으로 인스턴스화합니다(§ 14 보안 고려 사항).
예를 들어, 수준 1의 압축 해제기는 모든 등록 모듈을 네이티브로 구현한다면 엔진도 샌드박스도 필요하지 않으며, 수준 2의 압축 해제기는 둘 다 필요하지만 네이티브 구현은 필요하지 않습니다.
7.2. 리소스 블록
블록 6416부터 6716까지는 리소스 블록이며, 그 타입은 이진수로 0110 01ss입니다(§ 3.1.1 블록 유형). 이들의 데이터는 체크섬이 있는 Zstandard 데이터입니다(§ 3.2 공유 외부 구조).
압축 해제 후의 내부 구조는 해석되지 않은 바이트열이며, 이를 참조하는 공식 블록에 의해 모듈에 제공됩니다(§ 6.2 공식 데이터, § 7.3 모듈 실행).
7.3. 모듈 실행
이 섹션은 잠정적이며 docs/xform-design.md §5를 따릅니다.
모듈은 wasm_module로 저장된 WebAssembly 모듈 [WASM-CORE-3]이며, [WASM-CORE-3]의 부록에 정의된 임베딩 인터페이스를 통해 인스턴스화되고 호출됩니다. 모듈은 다음 프로필을 준수해야 하며, 그렇지 않으면 잘못된 아카이브 실패가 발생합니다:
-
WebAssembly 3.0 core이며, 결정적 프로필로 실행됩니다. 모든 NaN 결과는 정규 NaN입니다.
-
WebAssembly 3.0에서 추가된 기능은 다중 메모리와 작업 메모리 이외의 메모리의 64비트 주소를 제외하고는 어느 것도 허용되지 않습니다. 즉, 꼬리 호출, 확장 상수 표현식, 타입이 지정된 함수 참조, 가비지 컬렉션, 예외 처리, relaxed SIMD, 64비트 테이블, 64비트 작업 메모리는 허용되지 않습니다.
-
임포트된 테이블은 허용되지 않습니다.
-
시작 함수가 없습니다.
-
메모리를 정의하지 않으며, 각 메모리를 최대 한 번만 임포트합니다.
-
모든
memory.grow명령은 작업 메모리를 대상으로 합니다. -
작업 메모리 이외의 모든 메모리의 임포트 타입은 64비트 주소, 최솟값 0, 최댓값 없음을 가집니다.
-
함수
aqr_apply_whole과aqr_apply_part중 정확히 하나를 엔트리 포인트로 익스포트합니다. 전자의 경우 모듈은 전체 모듈이고, 후자의 경우 부분 모듈입니다. -
aqr_spawn을 임포트하면, 타입i32의 매개변수 두 개를 가지고 결과가 없는 함수aqr_task, 즉 태스크 엔트리 포인트를 익스포트하며, 이름이aqr_task_slots인 사용자 정의 섹션을 정확히 하나 가지고, 그 내용은 1에서 64까지의 바이트 하나, 즉 태스크 슬롯입니다. 그렇지 않으면 해당 이름의 사용자 정의 섹션을 가지지 않습니다.
Zstandard Window_Size(§ 3.2.1 Zstandard 압축 방식)에 대해 보장되는 227바이트(128 MiB) 한도를 메모리 한도에도 적용해야 하는지 여부.
모듈의 모든 임포트는 모듈 이름 aqr에서 다음 이름 중 하나로 이루어집니다. 임포트와 익스포트의 이름에는 이들을 하나의 네임스페이스로 링크하는 툴체인과 호스트에서의 충돌을 피하기 위해 aqr_ 접두사가 붙습니다. 그 밖의 임포트는 잘못된 아카이브 실패입니다.
aqr_memory-
메모리로, 작업 메모리입니다.
aqr_params-
매개변수의 메모리입니다.
aqr_resourcei-
i번째 리소스의 메모리이며, 여기서 i는 앞자리 0이 없는 십진수입니다.
aqr_inputk-
k번째 입력의 메모리이며, 여기서 k는 앞자리 0이 없는 십진수입니다.
aqr_outputj-
j번째 출력의 메모리이며, 여기서 j는 앞자리 0이 없는 십진수입니다.
aqr_request,aqr_release,aqr_commitaqr_heartbeat-
아래에 정의된 함수입니다.
aqr_spawn,aqr_result,aqr_join,aqr_take-
§ 7.3.1 태스크에 정의된 태스크의 함수입니다.
aqr_chan,aqr_send,aqr_recv,aqr_close-
§ 7.3.1 태스크에 정의된 채널의 함수입니다.
aqr_log-
아래에 정의된 진단 함수입니다.
메모리의 최솟값과 최댓값이 모두 ⌈L / 65536⌉ 페이지이면, 그 메모리는 길이 L에 크기가 맞춰진 것입니다.
입력 바이트열의 리스트 inputs, 리소스 블록의 리스트 resources, 매개변수 바이트열 params, 선언된 출력 크기의 리스트 sizes, 그리고 메모리 한도가 주어졌을 때 module에 대해 모듈을 적용하려면:
-
sizes는 비어 있어서는 안 됩니다.
-
임포트되는 모든
aqr_resourcei는 i < resources의 리소스 수를 만족해야 하고, 임포트되는 모든aqr_inputk는 k < inputs의 입력 수를 만족해야 하며, 임포트되는 모든aqr_outputj는 j < sizes의 크기 수를 만족해야 합니다. -
임포트된 메모리를 생성하십시오. 각 메모리는 처음에 0으로 채워집니다:
-
작업 메모리는 임포트 타입의 최소 크기를 가집니다.
-
매개변수의 메모리는 params의 길이에 크기가 맞춰진 것이며, 오프셋 0부터 params를 담습니다.
-
k번째 입력의 메모리는 inputs[k]의 길이에 크기가 맞춰집니다. module이 전체 모듈이면, 그 메모리는 오프셋 0부터 inputs[k]를 담습니다. 그렇지 않으면 Wk를 그 메모리의 바이트 단위 크기라고 하고, k번째 입력의 가시 범위를 [0, 0)이라고 합니다.
-
j번째 출력의 메모리는 sizes[j]에 맞게 크기가 정해집니다. module이 부분 모듈이면, Wout,j를 그 메모리의 바이트 단위 크기라고 합니다.
-
-
메모리들의 바이트 단위 총 크기가 메모리 한도를 초과하면 리소스 실패를 반환합니다. 메모리 한도는 애플리케이션의 메모리(그 태스크의 메모리 포함)와 그 채널에 대기 중인 메시지의 총 크기에 적용됩니다.
-
아래의 메모리와 호스트 함수로 module을 인스턴스화합니다.
-
i64타입의 인수를 다음 순서대로 전달하여 엔트리 포인트를 호출합니다: params의 길이, resources(리소스)의 각 항목의 저장된 내용의 길이, inputs의 각 항목의 길이와 sizes의 각 항목. 엔트리 포인트는 이 매개변수들을 가져야 하며 결과는 없어야 합니다. 진입점이 트랩되거나 아래의 호스트 함수 중 하나라도 오류를 알리면 잘못된 아카이브 실패를 반환합니다. 진입점이 메모리 한도나 구현 정의 한도를 초과하면 리소스 실패를 반환합니다. 애플리케이션의 태스크 중 하나라도 실패하면 그 실패를 반환합니다(여러 개가 실패하면 그중 어느 것의 실패든 반환합니다). 애플리케이션의 모든 인스턴스가 대기하면 잘못된 아카이브 실패를 반환합니다. 진입점이 반환되면, 진입점이 생성한 모든 태스크는 회수된 상태여야 합니다. -
module이 전체 모듈이면, 각 outputs[j]를 j번째 출력의 메모리가 임포트된 경우 그 메모리의 처음 sizes[j] 바이트로, 그렇지 않으면 sizes[j]개의 0 바이트로 설정합니다.
-
각 outputs[j]의 길이는 sizes[j]여야 합니다.
-
outputs를 반환합니다.
부분 모듈에서 데이터보다 작은 윈도, 즉 링 버퍼. 이는 최대값이 있는 임포트 타입을 허용할 것입니다.
memory.grow 명령은 애플리케이션의 메모리(그 태스크의 메모리와 그 채널에 대기 중인 메시지 포함)의 총 크기가 메모리 한도를 초과하게 될 때 메모리 한도를 초과하며, 그렇지 않으면 작업 메모리의 임포트 타입의 최대값이 허용하는 경우 성공해야 합니다.
호스트 함수는 다음과 같이 정의됩니다. 여기서 lok와 hik는 k번째 입력의 가시 범위를 나타내고, k, j, ptr, len은 i32 타입이며, 모든 오프셋은 i64 타입입니다. 태스크가 호출하면 aqr_request, aqr_release, aqr_commit은 오류를 알립니다.
- aqr_request(k, hi)
-
k번째 입력은 임포트되어야 합니다. hi는 [hik, inputs[k]의 길이] 범위에 있어야 하며, hi - lok는 최대 Wk여야 합니다. [hik, hi)의 각 오프셋 o에 대해, inputs[k][o]를 k번째 입력의 메모리의 o % Wk 위치에 씁니다. 그런 다음 hik를 hi로 설정합니다.
- aqr_release(k, lo)
-
k번째 입력은 임포트되어야 합니다. lo는 [lok, hik] 범위에 있어야 합니다. lok를 lo로 설정합니다.
- aqr_commit(j, hi)
-
j번째 출력은 임포트되어야 합니다. hi는 [outputs[j]의 길이, sizes[j]] 범위에 있어야 하며, hi - outputs[j]의 길이는 최대 Wout,j여야 합니다. [outputs[j]의 길이, hi) 범위의 각 오프셋 o에 대해, j번째 출력의 메모리에서 o % Wout,j 위치의 바이트를 outputs[j]에 덧붙입니다.
- aqr_heartbeat()
-
아무 동작도 하지 않습니다.
- aqr_log(ptr, len)
-
아무 동작도 하지 않습니다. 구현은 작업 메모리의 오프셋 ptr부터 len바이트를 진단 메시지로 표시할 수 있으며, 이는 아카이브에서 온 신뢰할 수 없는 데이터입니다. 다만 그렇게 하는 것이 애플리케이션에 영향을 주어서는 안 되며, 범위가 경계를 벗어나는 경우에도 마찬가지입니다. 이것은 유일한 진단 함수입니다. 모듈은
aqr_log다음에 트랩을 발생시켜 실패를 보고합니다.
호스트 함수의 MUST 요구 사항 중 어느 하나라도 위반하면 모듈 호출에 오류로 알려집니다.
7.3.1. 태스크
태스크는 모듈의 독자적인 인스턴스로, 자신을 생성한 인스턴스의 작업 메모리 사본 위에서 실행되며 바이트열, 즉 자신의 결과를 돌려줍니다. 애플리케이션의 인스턴스와 모든 태스크는 애플리케이션의 인스턴스입니다. 각 인스턴스는 처음에 비어 있는 핸들의 리스트를 가지며, 각 핸들은 한 번에 생성된 태스크의 리스트입니다. 태스크는 보류 중, 실행 중, 완료됨(결과와 함께), 실패함(잘못된 아카이브 실패 또는 리소스 실패와 함께), 또는 회수됨 중 하나의 상태입니다.
인스턴스가 생성한 태스크 중 동시에 실행 중인 것은 최대 모듈의 태스크 슬롯(모듈의 사용자 정의 섹션 aqr_task_slots) 수만큼입니다. 태스크는 생성된 때부터 시작될 때까지 보류 중입니다. 그 인스턴스가 생성한 태스크 중 실행 중인 것이 태스크 슬롯보다 적을 때마다, 생성된 순서(핸들 순, 그다음 인덱스 순)에 따라 그중 보류 중인 첫 번째 태스크가 시작됩니다. 그 태스크는 그때부터 실행 중이며, 실행됩니다. 실행 중인 태스크는 완료되거나 실패하면 실행 중 상태가 끝납니다. 보류 중인 태스크는 아직 애플리케이션의 인스턴스가 아닙니다. 메모리가 없으며 대기하지 않습니다.
채널은 애플리케이션의 인스턴스들을 연결하며, 최대 용량만큼의 메시지(바이트열)로 이루어진 큐, 닫힘 여부, 그리고 처음에는 없는 인스턴스인 송신자와 수신자로 구성됩니다. 애플리케이션은 처음에 비어 있는 채널의 리스트를 가집니다. 인스턴스가 종료되면(반환하거나 실패하면), 그 인스턴스가 송신자인 모든 채널이 닫힙니다. 인스턴스는 aqr_join, aqr_send 또는 aqr_recv 안에 있고 기다리는 조건이 성립하지 않는 동안 대기합니다.
바이트열 S, arg, i가 주어졌을 때 태스크를 실행하려면 다음과 같이 합니다:
-
임포트되는 메모리를 생성합니다. 작업 메모리는 S.length / 65536 페이지와 그 임포트 타입의 최대값을 가지며, 다른 모든 메모리는 최소값과 최대값으로 0을 가집니다.
-
메모리와 호스트 함수로 모듈을 인스턴스화한 다음, 작업 메모리의 내용을 S로 설정합니다.
-
arg와 i로 태스크 엔트리 포인트를 호출합니다. 트랩이 발생하거나 어떤 호스트 함수가 오류를 알리면, 태스크는 잘못된 아카이브 실패로 실패합니다. 메모리 한도나 구현 정의 한도를 초과하면, 태스크는 리소스 실패로 실패합니다.
-
반환된 후에는 그것이 생성한 모든 태스크가 회수됨 상태여야 하며, 그렇지 않으면 태스크는 잘못된 아카이브 실패로 실패합니다.
-
태스크는 aqr_result로 마지막에 설정된 결과를, 설정된 것이 없으면 빈 바이트열을 결과로 하여 완료됩니다.
태스크의 호스트 함수는 이를 호출하는 인스턴스 I에 대해 다음과 같이 정의되며, 여기서 arg, n, h, i, ptr, dst, len은 부호 없는 i32 타입입니다:
- aqr_spawn(arg, n)
-
S를 I의 작업 메모리의 내용이라고 합니다. I의 핸들에 n개의 새 대기 중 태스크로 이루어진 리스트를 추가합니다. 그중 (0부터 센) i번째 태스크는 (태스크 슬롯이 허용하는 대로) 시작되면 S, arg, i를 받아 태스크를 실행합니다. 그리고 I의 핸들 중에서 그 리스트의 인덱스를 반환합니다.
- aqr_result(ptr, len)
-
I는 태스크여야 하며, ptr + len은 최대 그 작업 메모리의 크기여야 합니다. I의 결과를 그 작업 메모리의 오프셋 ptr부터 len 바이트로 설정합니다.
- aqr_join(h, i)
-
I의 h번째 핸들의 i번째 태스크는 존재해야 하며 회수된 상태가 아니어야 합니다. 그 태스크가 완료되거나 실패할 때까지 대기합니다. 그 태스크가 실패했다면 인스턴스 I는 그 실패로 실패합니다. 그렇지 않으면 그 결과의 길이를 반환합니다.
- aqr_take(h, i, dst, len)
-
I의 h번째 핸들의 i번째 태스크는 완료(조인)된 상태여야 하고, len은 최대 그 결과의 길이여야 하며, dst + len은 최대 I의 작업 메모리의 크기여야 합니다. 그 결과의 처음 len 바이트를 I의 작업 메모리의 오프셋 dst부터 복사하며, 그 태스크는 회수됩니다.
채널의 호스트 함수는 다음과 같이 정의됩니다. 여기서 c, ptr, dst, len, cap은 부호 없는 i32 타입이고, 채널 C는 애플리케이션의 (0부터 센) c번째 채널로서 존재해야 하며, I에 대해 C의 한쪽 끝을 취한다는 것은 다음을 뜻합니다: 그 끝이 없음이면 그 끝을 I로 설정하고, 그렇지 않으면 그 끝은 I여야 합니다.
- aqr_chan(cap)
-
cap은 1 이상이어야 합니다. 애플리케이션의 채널에 용량이 cap인 새 채널을 추가하고, 그 인덱스를 반환합니다.
- aqr_send(c, ptr, len)
-
I에 대해 C의 송신자를 취합니다. C는 닫혀 있어서는 안 되며, ptr + len은 I의 작업 메모리 크기 이하여야 합니다. 대기 중인 메시지가 C의 용량보다 적어질 때까지 기다린 다음, I의 작업 메모리에서 오프셋 ptr부터 len 바이트를 큐에 넣습니다.
- aqr_recv(c, dst, cap)
-
I에 대해 C의 수신자를 취합니다. 메시지가 큐에 들어오거나 C가 닫힐 때까지 대기합니다. 큐에 메시지가 없으면 −1(0xFFFFFFFF)을 반환합니다. 그렇지 않으면 M을 첫 번째 메시지라고 합니다. M의 길이가 cap을 초과하면 M을 큐에 남겨 둔 채 그 길이를 반환합니다. 그렇지 않으면 dst + M의 길이는 I의 작업 메모리 크기 이하여야 합니다. M을 큐에서 꺼내 I의 작업 메모리의 오프셋 dst부터 복사하고, 그 길이를 반환합니다.
- aqr_close(c)
-
I에 대해 C의 송신자를 취하고, C를 닫습니다.
작업 메모리 이외의 메모리를 읽기 전용으로 태스크에 보이게 해야 하는지 여부(이 경우 그 메모리에 쓰는 명령을 거부해야 합니다), 그리고 태스크에서의 부분 모듈의 윈도입니다.
8. 오버레이 아카이브와 임포트
8.1. 임포트 블록
블록 5816부터 5F16까지는 임포트 블록이며, 이는 세그먼트 외부에 있는 파일의 바이트 범위를 제공하는 비리프 블록입니다. 그 타입은 다음과 같으며 0101 1ess(이진 표기)(§ 3.1.1 블록 유형)이며, 이들의 데이터는 체크섬이 있는 Zstandard 데이터입니다(§ 3.2 공유 외부 구조).
e는 출처(외부)를 결정합니다. 0이면 선행 세그먼트이고, 1이면 베이스입니다.
임포트 블록은 external이 거짓이면 내부 임포트 블록이고, 그렇지 않으면 외부 임포트 블록입니다. 외부 임포트 블록은 오버레이 아카이브 이외의 아카이브에 나타나서는 안 됩니다 (§ 3.5 암호화되지 않은 아카이브).
압축 해제 후의 내부 구조는 다음과 같습니다:
| 이름 | 타입 |
|---|---|
| num_imports | leb128 ≠ 0 |
| imports | 임포트[num_imports] |
각 임포트는 다음으로 구성됩니다:
| 이름 | 타입 |
|---|---|
| import_path | u8[*] ≠ 0 |
| import_path_sentinel | u8 = 0 |
| import_offset | leb128 |
| import_size | leb128 |
| import_checksum | u8[32] |
내부 또는 외부 임포트 블록에 있는 임포트는 각각 내부 임포트 또는 외부 임포트입니다. 각 임포트는 하나의 비리프 항목을 제공하므로, 임포트 블록은 imports의 순서대로 num_imports개의 항목을 제공합니다. 항목의 저장된 내용은 임포트를 해석하여 얻는 그 임포트의 임포트된 내용으로, import_path에서 찾은 파일의 [import_offset, import_offset + import_size) 범위의 바이트이며, 다음과 같습니다.
-
내부 임포트는 자신의 세그먼트의 선행 결과, 즉 모든 선행 세그먼트를 베이스에 적용한 결과에서 파일을 찾으며, 이 결과는 독립 아카이브의 경우 비어 있습니다.
-
외부 임포트는 주어진 그대로의 베이스에서 파일을 찾습니다.
import_path는 2F16 (/)으로 구분된 0개 이상의 구성 요소로 이루어진 시퀀스입니다. 각 구성 요소는 entry_name과 같은 인코딩을 사용하며, 디코딩된 이름은 엔트리 이름과 같은 제한을 받으므로 어떤 구성 요소도 비어 있을 수 없습니다. 빈 import_path는 루트 자체를 나타내며, 베이스가 단일 파일이면 루트는 파일입니다.
import_checksum은 임포트된 내용의 SHA-512/256 해시 [FIPS180-4]입니다.
/가 포함될 수 없고 . 또는 ..일 수도 없으므로, import_path를 나누는 방식은 모호하지 않으며 어떤 경로도 루트 바깥의 것을 가리킬 수 없습니다. 경로는 그것을 사용하는 파일의 디렉터리가 아니라 루트를 기준으로 합니다. 임포트를 참조하는 공식 블록은 메타데이터 블록이 해당 파일에 이름을 붙이기 전에 평가되며, 하나의 리프 블록을 서로 다른 디렉터리의 파일들이 공유할 수 있기 때문입니다. 같은 블록 안의 경로들이 공유하는 접두사는 잘 압축됩니다.
8.2. 임포트 해석
세그먼트에 있는 임포트 블록의 import를 임포트 해석하려면 다음을 수행합니다.
-
import.import_path가 비어 있지 않으면 이를 모든 2F16에서 구성 요소로 분할하고, 각 구성 요소를 엔트리 이름으로 디코딩한 결과를 names라 합니다. 어떤 구성 요소라도 디코딩에 실패하면 잘못된 아카이브 실패를 반환합니다.
-
import가 내부 임포트이면 세그먼트의 앞선 결과를, 그렇지 않으면 아카이브 베이스의 루트를 node라 합니다.
-
names의 각 name에 대해 순서대로 다음을 수행합니다:
-
node는 파일 노드여야 합니다.
-
import.import_offset + import.import_size는 node.content의 길이 이하여야 합니다.
-
[import.import_offset, import.import_offset + import.import_size) 범위에 있는 node.content의 바이트를 content라 합니다.
-
content의 SHA-512/256 해시 [FIPS180-4]는 import.import_checksum과 같아야 합니다.
-
content를 import의 임포트된 내용으로 반환합니다.
이 알고리즘의 MUST 중 어느 하나라도 위반하면 오버레이 아카이브에서는 베이스 실패이고, 독립 아카이브에서는 잘못된 아카이브 실패입니다.
8.3. 베이스 읽기
파일 시스템에서 베이스를 읽는 것은 추출과 마찬가지로 아카이브 모델의 범위 밖입니다. 베이스를 읽는 압축 해제기는 디렉터리 또는 단일 파일을 아카이브 값에 대응시키며, 다음 요구 사항을 따릅니다.
-
명시적으로 베이스가 주어지지 않는 한 베이스를 사용해서는 안 되며, 특히 현재 디렉터리나 대상 디렉터리를 기본값으로 베이스로 사용해서는 안 됩니다.
-
베이스에 이미 존재하는 심볼릭 링크나 기타 리디렉션을 통한 경우를 포함하여, 주어진 디렉터리 밖의 어떤 것도 읽어서는 안 됩니다. 베이스 안의 심볼릭 링크는 링크 노드이며 결코 따라가지 않으므로, 경로가 링크를 거치거나 링크로 이어지는 임포트는 해석에 실패합니다(§ 8.2 임포트 해석).
-
일반 파일이 아닌 파일을 파일 노드의 내용으로 읽어서는 안 됩니다. 베이스 안의 FIFO나 장치 파일은 특수 노드입니다.
-
파일 시스템 이름을 § 12 추출의 매핑의 역으로 엔트리 이름에 매핑하는 것이 좋습니다. 이름을 매핑할 수 없거나 여러 이름이 같은 엔트리 이름에 매핑되는 경우, 실패하거나 이를 사용자에게 보고해야 합니다.
-
베이스는 마치 한 시점에 읽힌 것처럼 해석 내내 동일하게 보여야 합니다. 압축 해제기는 베이스 중 임포트와 결과에 필요한 부분만 읽을 수 있으며, 임포트의 체크섬은 읽는 동안 변경된 부분을 검출합니다.
압축 해제기는 오버레이 아카이브의 결과를 그 베이스 디렉터리에 쓸 수 있습니다. 즉, 제자리에서 적용할 수 있습니다. 이 경우에는 다음과 같습니다.
-
결과는 모든 임포트된 내용을 어떤 것이든 쓰기 전에 읽은 것과 같아야 합니다. 예를 들어 새 파일을 임시 이름으로 쓰고 모두 쓴 뒤에 이름을 바꾸는 방식이 있습니다.
-
베이스의 노드는 그 노드가 결과에 없을 때에만 제거할 수 있으며, 어떤 세그먼트에서도 주어지지 않은 모든 노드는 그대로 두는 것이 좋습니다.
9. 암호화 아카이브
아카이브는 암호화되어 있을 수 있으며, 이 경우 아카이브는 ENCRYPTED_HEADER_TYPE 타입의 블록 형태 헤더와 그 뒤에 오는 암호화된 청크들로 구성됩니다. 암호화 아카이브가 ENCRYPTED_MIRRORED_HEADER_MAGIC으로 시작하면 미러링된 것이며, 이 경우 헤더의 복사본이 암호화된 청크들 사이에 배치됩니다.
| 이름 | 타입 | 조건 |
|---|---|---|
| checksum_start | offset | |
| encrypted_header_magic | u8[8] ∈ {ENCRYPTED_HEADER_MAGIC, ENCRYPTED_MIRRORED_HEADER_MAGIC} | |
| lg_work_factor | u8 ∈ [0C16, 3F16) | |
| scrypt_ephemeral_secret | u8[16] | |
| encrypted_file_key | u8[32] | |
| age_header_mac | u8[32] | |
| payload_nonce | u8[16] | |
| checksum_end | offset | |
| header_checksum | u64le | If mirrored |
| chunks | u8[*] |
lg_work_factor부터 payload_nonce까지의 97바이트를 헤더 필드라고 합니다.
header_checksum은 checksum_start부터 checksum_end까지의 모든 바이트에 대해 시드 0으로 계산한 XXH64 [XXHASH] 해시입니다.
lg_work_factor부터 age_header_mac까지의 모든 필드는 다음과 같이 age v1 텍스트 파일 헤더 [AGE]를 재구성하는 데 사용됩니다.
age-encryption.org/v1 -> scrypt scrypt_ephemeral_secret_base64 lg_work_factor_decimal encrypted_file_key_base64 --- age_header_mac_base64
여기서,
-
scrypt_ephemeral_secret_base64는 scrypt_ephemeral_secret을 패딩 없이 base64로 인코딩한 것입니다([RFC4648], 4절).
-
lg_work_factor_decimal은 lg_work_factor를 앞자리 0 없이 십진수로 표현한 것입니다.
-
encrypted_file_key_base64는 encrypted_file_key를 패딩 없이 base64로 인코딩한 것입니다.
-
age_header_mac_base64는 age_header_mac을 패딩 없이 base64로 인코딩한 것입니다.
age 페이로드는 payload_nonce와 그 뒤에 오는 chunks 안의 암호화된 청크들로 구성되며, age v1 형식에 따라 해석됩니다. 마지막 청크를 제외한 모든 암호화된 청크의 길이는 65,552바이트입니다. 즉, 64 KiB의 평문 뒤에 16바이트 인증 태그가 붙은 것입니다.
아카이브가 mirrored가 아니면 chunks는 암호화된 청크만으로 구성됩니다. 그렇지 않으면 chunks에는 미러 헤더도 포함됩니다. 0부터 번호를 매긴 각 암호화된 청크 Cm에 대해, m + 1이 2부터 32까지의 2의 거듭제곱이거나 64의 양의 배수인 경우에만 미러 헤더가 Cm 바로 앞에 옵니다. 따라서 1부터 번호를 매긴 k번째 미러 헤더는 Cm 앞에 오며, 여기서 k ≤ 6이면 m = 2k − 1이고 k ≥ 6이면 m = 64(k − 5) − 1입니다. 또한 마지막 암호화된 청크 뒤에는 미러 헤더가 오지 않습니다. 미러 헤더는 다음으로 구성됩니다.
| 이름 | 타입 |
|---|---|
| checksum_start | offset |
| encrypted_mirror_magic | u8[8] = ENCRYPTED_MIRROR_MAGIC |
| mirror_index | u32le = k |
| mirrored_fields | u8[97] = 헤더 필드 |
| checksum_end | offset |
| mirror_checksum | u64le |
mirror_checksum은 checksum_start부터 checksum_end까지의 모든 바이트에 대해 시드 0으로 계산한 XXH64 [XXHASH] 해시입니다.
미러 블록과 마찬가지로, 미러링된 헤더의 필드에 대한 제약은 적합한 압축기에 대한 요구 사항입니다. 미러링된 헤더는 그 위치에서 항상 117바이트를 차지하며, 그 내용은 아카이브 해석의 결과에 영향을 주지 않습니다. 미러링된 헤더는 그 mirror_checksum이 검증되고 mirrored_fields 이외의 필드가 위의 제약을 만족하면 온전합니다. 압축 해제기는 온전하지 않은 미러링된 헤더의 mirrored_fields를 사용해서는 안 됩니다.
64개의 암호화된 청크라는 간격은 § 10.3 미러 블록의 잠정적인 4 MiB 거리를 따르며, 그 거리가 바뀌면 함께 바뀝니다(docs/recovery-design.md §5.5).
age 페이로드는 유효한 암호화되지 않은 아카이브(§ 3.5 암호화되지 않은 아카이브), 즉 독립 아카이브 또는 오버레이 아카이브로 복호화되어야 하며, 다음과 같은 추가 제약이 적용됩니다.
-
size_hint_block은 더 이상 선택 사항이 아닙니다.
-
암호화 아카이브의 제자리 수정을 억제하기 위해 스페이서 블록(§ 3.3 스페이서 블록)은 허용되지 않습니다.
-
아카이브 뒤에는 어떤 뒤쪽 바이트도 허용되지 않습니다.
10. 무결성과 복구
10.1. 체크섬
다음 표는 이 명세에서 정의하는 모든 체크섬을 요약합니다.
| 구조 | 체크섬 | 계산 대상 | 존재 여부 |
|---|---|---|---|
| 약칭 블록을 제외한 메타데이터, 사전, 임포트, 모듈 및 리소스 블록 | XXH64 | 디코딩된 데이터 | 항상 |
| 약칭 블록 | 없음(등록 해시는 엔트리를 식별하지만 검사되지는 않습니다) | 없음 | |
| 비리프 데이터 블록 | XXH64 | 디코딩된 데이터 | 항상 |
| 리프 데이터 블록 | XXH64 | 디코딩된 데이터 | 유형에 따라 다름 |
| 공식 블록의 formula_data | XXH64 | 디코딩된 데이터 | 항상 |
| 공식 내의 바이트열 | XXH64 (Checksum 명령) | 대상이 되는 바이트열 | 선택 |
| 임포트 | SHA-512/256 (import_checksum) | 임포트된 내용 | 항상 |
| 미러 블록 | XXH64 (mirror_checksum) | 저장된 바이트 | 항상 |
| 암호화 아카이브의 헤더와 미러링된 헤더 | XXH64 (header_checksum, mirror_checksum) | 저장된 바이트 | 미러링된 경우 |
암호화 아카이브의 암호화된 청크는 [AGE]에 의해서도 인증됩니다.
명시적으로 달리 요청되지 않는 한, 적합한 압축기는 모든 리프 블록에 None이 아닌 checksum_method를 사용하는 것이 좋습니다.
명시적으로 달리 요청되지 않는 한, 적합한 압축기는 모든 공식 블록에 출력 체크섬을 선언하는 것이 좋습니다.
-
체크섬이 없는 압축되지 않은 리프 블록은 파일 내용을 그대로 담고 그로부터 파생된 것은 아무것도 담지 않으므로, 파일을 아카이브에서 메모리 매핑하여 제자리에서 수정할 수 있습니다.
-
체크섬이 없는 압축된 리프 블록은 기존 Zstandard 프레임의 Zstandard 블록을 복사하여 만들 수 있으며, 디코딩된 데이터에 대한 체크섬을 계산하기 위해 그 블록들의 압축을 해제할 필요가 없습니다.
어느 쪽도 장기 보존에서는 고려 대상이 아니며, 장기 보존에서는 보존 프로필이 체크섬을 요구합니다.
10.2. 보존 프로필
모든 파일의 내용이 체크섬으로 보호될 때 아카이브는 보존 프로필을 준수합니다.
-
모든 리프 데이터 블록은 None이 아닌 checksum_method를 가집니다.
-
아카이브는 독립 아카이브입니다.
-
어떤 블록도 약칭 블록(§ 4.2.2 등록 사전과 등록 모듈)이 아닙니다.
장기 보존용 아카이브를 생성하는 적합한 압축기는 보존 프로필을 준수하는 것이 좋습니다. 아카이브를 검증하는 도구는 해당 아카이브가 보존 프로필을 준수하는지 보고하는 것이 좋습니다.
10.3. 미러 블록
미러 블록은 대상 블록이라고 하는 다른 블록의 그대로의 사본을 담아, 대상 블록이 손상되었을 때 이를 복원할 수 있게 합니다. 미러 블록은 손상이 둘 이상의 파셀에 영향을 미치고 해당 블록만 읽어서 감지할 수 있는, 즉 체크섬을 가진 블록 유형에 대해서만 정의됩니다.
| 블록 유형 | 대상 블록 유형 |
|---|---|
| C1016 — C1716 | 1016 — 1716 (§ 4.3 메타데이터 블록) |
| C5016 — C5716 | 5016 — 5716 (§ 5.2 사전 블록) |
| C5816 — C5F16 | 5816 — 5F16 (§ 8.1 임포트 블록) |
| C6016 — C6316 | 6016 — 6316 (§ 7.1 모듈 블록) |
| C6416 — C6716 | 6416 — 6716 (§ 7.2 리소스 블록) |
| C7016 — C7716 | 7016 — 7716 (§ 6.1 공식 블록) |
다른 모든 블록과 마찬가지로, 미러 블록의 size 필드는 자신의 type에 의해 결정되며 자신의 data의 바이트 수를 셉니다. 미러 블록은 대상 블록보다 길기 때문에, 그 size 필드가 더 넓어야 할 수도 있습니다. 따라서 미러 블록은 다음 중 하나입니다.
-
비확장: 미러 블록과 그 대상 블록의 size 필드의 너비가 같으며, 그 대상 타입은 자신의 type에서 C0016을 뺀 값입니다. 또는
-
확장: 미러 블록의 size 필드가 대상 블록의 것보다 한 단계 더 넓으며, 그 대상 타입은 자신의 type에서 C0116을 뺀 값입니다. 이 경우 그 type % 4는 0이어서는 안 됩니다.
대상 블록은 대상 타입의 블록이어야 하며, 이 타입은 미러 블록의 type에 대해 위에 나열된 블록 유형이어야 합니다. 미러 블록이 확장되었는지 여부는 target_offset으로 나타냅니다.
그 데이터는 다음으로 구성됩니다.
| 이름 | 타입 |
|---|---|
| mirror_magic | u8[8] ∈ {MIRROR_MAGIC, OVERLAY_MIRROR_MAGIC} |
| archive_offset | leb128 = block_start - archive_start |
| target_offset | leb128 ≠ 0 |
| target_data | u8[*] |
| checksum_end | offset |
| mirror_checksum | u64le |
mirror_magic은 헤더, 푸터, 메타데이터 체인과 같은 주변 구조가 손상되었을 때 스캔으로 미러 블록을 찾을 수 있게 해 주는 고정된 바이트열입니다. 이 값은 오버레이 아카이브에서는 OVERLAY_MIRROR_MAGIC이어야 하며, 그 밖의 경우에는 MIRROR_MAGIC이어야 합니다.
archive_offset은 archive_start에서 미러 블록의 block_start까지의 오프셋입니다.
target_offset은 대상 블록의 block_start에서 미러 블록의 block_start까지의 오프셋이며, 미러 블록이 확장된 경우에는 여기에 1을 더한 값입니다. 임의의 두 블록 사이의 오프셋은 짝수이므로, target_offset % 2로 미러 블록이 확장되었는지 알 수 있습니다. 대상 블록은 항상 미러 블록보다 앞에 있습니다.
target_data는 대상 블록의 데이터의 사본입니다. 대상 블록의 block_start부터 block_end까지의 바이트는 다음을 이어 붙인 것과 같아야 합니다.
-
대상 타입의 크기 필드로 인코딩한 target_data의 길이
대상 블록의 패딩은, 있더라도 비교하지 않으며 0 바이트로 복원됩니다. 모든 블록 유형은 가장 짧은 leb128 인코딩을 사용하므로, 미러 블록 자체의 type은 2바이트 길이입니다.
mirror_checksum은 미러 블록의 block_start부터 checksum_end까지, 타입 필드와 크기 필드를 포함한 모든 바이트에 대해 시드 0으로 계산한 XXH64 [XXHASH] 해시입니다.
미러 블록은 data가 대상 타입의 size 필드에 들어가지 않는 경우가 아니라면 확장되지 않은 상태인 것이 좋습니다.
미러 블록은 어떤 세그먼트에도 속하지 않으며, 대상 블록이 그보다 앞에 와야 한다는 요구 사항만 지킨다면 마지막 메타데이터 블록 뒤를 포함하여 블록 내 어디에나 나타날 수 있습니다. 하나의 대상 블록은 여러 개의 미러 블록을 가질 수 있습니다. 이 절에서 미러 블록에 대한 요구 사항은 적합한 압축기에 대한 요구 사항이며, 이를 위반하는 미러 블록이 있다고 해서 아카이브가 잘못된 아카이브가 되지는 않습니다. 미러 블록은 아카이브 해석의 결과에 전혀 영향을 주지 않습니다.
미러 블록은 mirror_checksum이 검증되고, 대상 블록과 같아야 한다는 것 외의 위 요구 사항을 충족하면 온전한 것입니다. 압축 해제기는 온전한 것이 아닌 미러 블록으로부터 대상 블록을 복원해서는 안 되며, 온전하지 않거나 온전한 대상 블록과 다른 미러 블록을 읽으면 이를 보고하는 것이 좋습니다.
미러 블록과 대상 블록이 서로 가까이 있으면 함께 손상될 것으로 예상됩니다. 적합한 압축기는 대상 블록의 끝과 그 미러 블록의 block_start 사이를 최소 4096바이트 떨어뜨려 기반 저장 장치의 정렬된 4 KiB 단위를 결코 공유하지 않도록 하는 것이 좋으며, 가능하면 최소 4 MiB(4,194,304바이트) 떨어뜨리는 것이 좋습니다.
권장 거리는 잠재 섹터 오류의 버스트 길이 측정(docs/recovery-design.md §8, R1)이 이루어질 때까지 잠정적인 값입니다. 암호화 아카이브(§ 9 암호화 아카이브)에서 미러링된 헤더 사이의 간격은 뒤의 거리에 따라 달라집니다.
손상된 암호화된 청크는 청크 전체가 인증에 실패하며, 하부 저장 장치의 정렬된 4 KiB 단위는 인접한 두 암호화된 청크에 걸쳐 있을 수 있습니다. 따라서 적합한 압축기는 암호화 아카이브에서 위에서 권장한 4096바이트의 거리 대신, 미러 블록과 그 대상 블록 사이에 적어도 하나의 온전한 암호화된 청크가 놓이도록 배치하는 것이 좋습니다.
10.4. 복구
복구 과정은 이 절에서 규정될 예정입니다(docs/recovery-design.md §5.6).
미러 블록이나 미러 헤더가 있는 암호화 아카이브를 복구하려면 인증에 실패한 청크 이후의 암호화된 청크를 복호화해야 하지만, 표준 age 구현은 이를 수행하지 않습니다. 이는 복구 과정과 함께, 복호화된 바이트를 생성하는 별도의 단계로 규정될 예정이며, 이 단계에서 그러한 청크의 바이트는 소거로 표시되고 아카이브 해석은 변경되지 않습니다(docs/recovery-design.md §5.6).
11. 선택적 메타데이터
메타데이터 블록(§ 4.3 메타데이터 블록)의 optional_metadata 필드는 CBOR [RFC8949]로 인코딩된 선택적 메타데이터를 담습니다. 이 부분은 연관된 날짜와 시각, 접근 제어 정보, 주석 등 파일을 읽는 데 꼭 필요하지는 않은 모든 메타데이터를 인코딩합니다.
선택적 메타데이터는 다음 CDDL [RFC8610] 정의로 기술됩니다:
aqr-opt-metadata= { aqr-archive-comment ? &(entry-extras: -2) => [* aqr-entry-extra / null] ; Indexed by entry numbers, starting from the root directory * $$aqr-opt-metadata-member ; For future extensions }$$aqr-opt-metadata-member= () ; No extension is defined by this specificationaqr-entry-extra= { aqr-entry-timestamps aqr-entry-comment aqr-entry-link-group aqr-entry-windows aqr-entry-unix aqr-entry-zip * $$aqr-entry-extra-member ; For future extensions }$$aqr-entry-extra-member= () ; No extension is defined by this specification ; TODO: other extras for round trip
이는 뒤따르는 바이트가 없는 하나의 올바른 형식의 CBOR 데이터 항목 [RFC8949]이어야 하며, 그 안의 어떤 맵도 중복 키를 가질 수 없습니다. 이 명세가 정의하는 모든 키의 값은 해당 정의와 일치해야 합니다. optional_metadata_size가 0이면 선택적 메타데이터는 빈 맵 {}입니다.
aqr-entry-extra의 멤버는 용도별로 묶여 있으며, 각 그룹은 다음 절들에서 설명합니다. -1부터 -59까지의 aqr-entry-extra 키는 플랫폼 중립적입니다. -60부터 -69까지의 키는 Windows에, -70부터 -79까지는 Unix에, -80부터 -89까지는 macOS에, -90부터 -99까지는 기타 플랫폼에 특화되어 있습니다. -100부터의 키는 엔트리가 변환되기 전의 형식에 특화되어 있습니다.
11.1. 타임스탬프
aqr-entry-extra의 키 crtime, ctime, mtime, atime, arctime은 파일 시스템과 아카이브 형식이 파일, 디렉터리 또는 링크에 연관 짓는 타임스탬프를 기록합니다. 각각은 아래에서 설명하는 aqr-time이며, 원본에 해당 타임스탬프가 없으면 존재하지 않습니다.
aqr-entry-timestamps = (
? &(crtime: -1) => aqr-time
? &(ctime: -2) => aqr-time
? &(mtime: -3) => aqr-time
? &(atime: -4) => aqr-time
? &(arctime: -5) => aqr-time
)
crtime-
생성 시간(출생 시간이라고도 합니다): 파일이 해당 위치에 생성된 시점입니다. 이후에는 갱신되지 않지만, 파일을 복사하면 보통 복사한 시각을 가진 새 파일이 생성됩니다. 예로는 NTFS와 FAT의 생성 시간, HFS+와 APFS의 생성 날짜, ext4의
i_crtime과 Btrfs의otime이 있으며, 이들은 BSD와 macOS에서는st_birthtime으로, Linux에서는statx의stx_btime으로 노출됩니다. ctime-
상태 변경 시간: 파일의 메타데이터(예: 권한, 소유권 또는 링크 수)나 내용이 마지막으로 변경된 시점입니다. 이는 POSIX의
st_ctime이며, NTFS의 변경 시각(change time)과 HFS+의 속성 수정 날짜(attribute modification date)가 이와 유사합니다. POSIX는 이를 임의의 시간으로 설정하는 것을 허용하지 않으므로, 추출하는 압축 해제기는 보통 이를 복원할 수 없습니다. mtime-
수정 시간: 파일의 내용이 마지막으로 변경된 시점입니다. 디렉터리의 경우, 그 안에서 엔트리가 마지막으로 추가, 제거 또는 이름 변경된 시각입니다. 이는 사실상 모든 파일 시스템과 아카이브 형식이 가지고 있는 타임스탬프입니다.
atime-
접근 시간: 파일의 내용이 마지막으로 읽힌 시점입니다. 많은 시스템은 성능을 위해 이를 느긋하게 갱신하거나 전혀 갱신하지 않으며(예: Linux의
relatime과noatime, 그리고 비활성화할 수 있는 NTFS의 접근 시각 갱신), FAT는 날짜만 보관합니다. 아카이빙을 위해 파일을 읽는 것 자체가 이를 갱신할 수 있습니다. arctime-
백업 시간: 백업 소프트웨어가 기록한, 파일이 마지막으로 백업된 시점입니다. 파일 시스템은 이를 스스로 갱신하지 않습니다. 예로는 HFS와 HFS+의 백업 날짜(backup date)가 있으며, macOS에서는
getattrlist의ATTR_CMN_BKUPTIME으로 노출됩니다. 이는 원본이 저장한 시각이며, 아카이브 자체가 만들어진 시각이 아닙니다.
st_ctime, ZIP 추가 필드 0x000a의 Ctime, 7z와 RAR의 CTime은 모두 ctime이 아니라 crtime에 해당합니다. pax와 같은 POSIX 형식은 이를 상태 변경 시각의 의미로 사용하며, 이는 ctime에 해당합니다.
aqr-time은 시점 또는 로컬 날짜 및 시각을 양자화 간격 및 선택적인 시간대 정보와 함께 나타냅니다. 이는 다음 CDDL 정의로 기술되며, 여기서 etime은 [RFC9581]에 정의된 확장 시간(extended time)에 대한 CDDL 타입 이름입니다.
aqr-time= $aqr-base-time / ; UTC timestamp, scaled and quantized [$aqr-base-time] / ; Local time in unknown time zone, encoded like UTC (*) [$aqr-instant,offset: aqr-tz-offset] / ; UTC timestamp with time zone offset ($) etime / ; Extended time defined in [[!RFC9581]] [etime] ; Local time in unknown time zone, encoded like UTCaqr-tz-offset= -48..56 ; In 15 minutes, positive east of UTC as in ISO 9660$aqr-base-time= #6.100(int) ; 1d (FAT atime*), date as in [[!RFC8943]] $aqr-base-time /= $aqr-instant ; Can be given a time zone offset$aqr-instant= #6.121(int) ; 2s (FAT mtime*, exFAT atime$, ZIP*, CAB*) $aqr-instant /= int ; 1s (~ext3, HFS/HFS+, tar, ISO 9660$) $aqr-instant /= #6.122(int) ; 10ms (FAT ctime*, ISO 9660$, exFAT$) $aqr-instant /= #6.123(int) ; 1ms $aqr-instant /= #6.124(int) ; 1us (UDF) $aqr-instant /= #6.125(int) ; 100ns (NTFS, RAR*, 7z) $aqr-instant /= #6.126(int) ; 1ns (ext4, ZFS, Btrfs, APFS)
베이스 시간 값 $aqr-base-time은 정수이며, 태그가 없거나 다음과 같이 태그가 붙습니다. 정수에 스케일을 곱한 값은 1970-01-01T00:00:00Z 이후의 초 수이며, CBOR 태그 1 [RFC8949]에서와 같이 윤초를 세지 않습니다. 음수 값은 그 기준 시점 이전의 시각을 나타냅니다. 태그는 양자화 간격도 결정하며, 양자화 간격은 시간 값에 대한 상대값으로 § 15.4 양자화 간격에 규정되어 있습니다. #6.100은 1970-01-01 이후의 일 수 [RFC8943]이며, $aqr-instant가 아닌 유일한 베이스 시간 값입니다. 즉, 여기에는 시간대 오프셋을 지정할 수 없습니다.
| 인코딩 | 양자화 간격 (s) | 스케일 (s) | 예 |
|---|---|---|---|
#6.100(int)
| [0, 86400] | 86400 | FAT atime* |
#6.121(int)
| [0, 2] | 2 | FAT mtime, exFAT atime$, ZIP, CAB* |
int
| [0, 1] | 1 | ext3 이하, HFS/HFS+, tar, ISO 9660$ (디렉터리 레코드) |
#6.122(int)
| [0, 10-2] | 10-2 | FAT ctime*, ISO 9660$ (볼륨 디스크립터), exFAT$ |
#6.123(int)
| [0, 10-3] | 10-3 | - |
#6.124(int)
| [0, 10-6] | 10-6 | UDF |
#6.125(int)
| [0, 10-7] | 10-7 | NTFS, RAR*, 7z |
#6.126(int)
| [0, 10-9] | 10-9 | ext4, ZFS, Btrfs, APFS |
예시 열에서 *로 표시된 형식은 알 수 없는 시간대의 지역 시간을 저장하며(또는 저장할 수 있으며), $로 표시된 형식은 UTC로부터의 오프셋과 함께 지역 시간을 저장합니다.
$aqr-instant로서만 의미를 가지며, 일반적인 확장 시간의 축약형이 아닙니다.
1001({1: 1697641200, -14: 86400, -10: "+09:00"})).
aqr-time의 각 대안은 다음과 같은 의미를 가집니다:
$aqr-base-time-
시간대 정보가 없는 시점입니다.
[$aqr-base-time]-
알 수 없는 시간대의 지역 날짜와 시각으로, UTC인 것처럼 인코딩됩니다. 이는 단일한 시점을 식별하지 않습니다.
[$aqr-instant, aqr-tz-offset]-
단독
$aqr-base-time과 같은 시점에, 그것이 기록된 시간대의 오프셋을 ISO 9660과 같이 15분 단위로 덧붙인 것입니다. 오프셋은 UTC의 동쪽에서 양수입니다: UTC+x시간은 4x이고 UTC−x시간은 −4x입니다. 지역 시간은 베이스 시간 값에 오프셋의 900배를 초 단위로 더한 값입니다. etime-
다른 대안에 맞지 않는 모든 값을 위한 확장 시간 [RFC9581](태그 1001)입니다. 시간대 정보가 있는 경우 Time Zone Hint(키 -10 또는 10)로 주어지며, 양자화 간격은 § 15.4 양자화 간격에서 설명하는 키로 주어집니다.
[etime]-
알 수 없는 시간대의 지역 날짜와 시각으로, UTC인 것처럼 인코딩됩니다. 확장 시간은 시간대 힌트(키 -10 또는 10)를 포함해서는 안 되며, 시간 척도(키 -1, -13 또는 13)가 있다면 0(UTC)이어야 합니다.
aqr-time의 다른 대안이 같은 시간 값, 같은 양자화 간격, 같은 시간대 정보를 나타낸다면 etime 또는 [etime]을 사용해서는 안 됩니다. 시간대 힌트는 그것이 해당 오프셋과 같은 숫자 오프셋(예: "+09:00")일 때에만 aqr-tz-offset과 같습니다. 양자화 간격이 없는 확장 시간은 결코 다른 대안과 같지 않습니다.
aqr-time을 무시할 수 있습니다.
UTCTime의 분, NTP의 2-32초, SQL Server DATETIME의 1/300초), UDF에서처럼 15분의 배수가 아닌 시간대 오프셋도 마찬가지입니다. 이들은 파일 메타데이터에서 충분히 드물기 때문에 별도의 인코딩을 둘 가치가 없습니다.
서로 다른 인코딩의 두 aqr-time 값은 같은 시점을 나타내더라도 서로 다릅니다. 예를 들어 1과 122(100)은 양자화 간격이 다릅니다. 파일 시스템이나 다른 아카이브 형식의 타임스탬프를 변환하는 압축기는 그 정밀도를 보존하는 것이 좋습니다. 즉, 양자화 간격이 원본과 일치하는 인코딩을 사용하는 것이 좋습니다.
-
알 수 없는 시간대의 지역 시간인 DOS 날짜 및 시간을 사용하는 ZIP:
[121(848878577)] -
UTC의 확장 타임스탬프 추가 필드(0x5455)를 사용하는 ZIP:
1697724754 -
시간대 오프셋을 분 단위로 기록하는 git:
[1697724754, 36] -
UTC 오프셋이 있는 exFAT 수정 시간:
[122(169772475487), 36] -
NTFS:
125(16977247548700000) -
ext4:
126(1697724754870000000) -
시간대를 알 수 없는 지역 날짜인 FAT 접근 날짜:
[100(19649)] -
가장 가까운 초로 반올림하는 원본:
1001({1: 1697724755, -14: [0.5, 0.5]})
ZIP에서 가져온 타임스탬프는 § 16.2 ZIP의 타임스탬프에서 다룹니다.
11.2. 주석
aqr-opt-metadata의 archive-comment와 aqr-entry-extra의 comment는 각각 아카이브 전체와 각 엔트리에 붙는 자유 형식의 주석으로, 예를 들어 ZIP이나 RAR의 아카이브 주석과 파일 주석이 이에 해당합니다. 둘 다 aqr-text이며, 이는 유니코드 문자열, 짝이 없는 서로게이트를 포함하는 WTF-8 [WTF-8] 문자열, 또는 인코딩을 알 수 없는 바이트열 중 하나입니다:
aqr-archive-comment= ( ? &(archive-comment: -1) => aqr-text )aqr-entry-comment= ( ? &(comment: -6) => aqr-text )aqr-text= tstr / ; Unicode #6.273(bstr) / ; WTF-8, i.e. UTF-16 + surrogate compat bstr ; In unknown encoding
aqr-text는 tstr로 표현할 수 있는 경우, 즉 유니코드 스칼라 값의 시퀀스인 경우 그렇게 표현해야 합니다. #6.273(bstr) 안의 바이트 문자열은 유효한 UTF-8 문자열이 아닌 유효한 WTF-8 문자열이어야 합니다. 즉, 인코딩된 짝이 없는 서로게이트를 적어도 하나 포함해야 합니다. bstr은 그 인코딩을 알 수 없으므로, 우연히 유효한 UTF-8 문자열이더라도 이 요구 사항의 적용을 받지 않습니다.
ZIP에서 가져온 주석은 § 16.3 ZIP의 주석에서 다룹니다.
11.3. link-group: 하드 링크
aqr-entry-link-group = (
? &(link-group: -7) => int ; File nodes only
)
link-group은 파일 노드가 원래 같은 파일이었다는 것, 즉 Unix에서 같은 inode에 대한 하드 링크이거나 NTFS에서 같은 파일 레코드에 대한 하드 링크였다는 것을 기록합니다. 이는 다른 노드에는 아무런 영향이 없으며, 압축기는 다른 노드에 이를 부여하지 않는 것이 좋습니다. 음이 아닌 값은 세그먼트 로컬이며 같은 세그먼트 안에서만 같은 그룹을 나타내고, 음수 값은 아카이브 전역이며 모든 세그먼트에서 같은 그룹을 나타냅니다. 아카이브 값에서 최종적으로 정해지는 링크 그룹은 세그먼트 해석에 의해 결정됩니다.
압축기는 같은 파일이 아니었던 파일들에 같은 링크 그룹을 부여해서는 안 됩니다. 압축기는 다른 어떤 파일 노드도 가지지 않는 링크 그룹을 부여하지 않는 것이 좋습니다. link-group이 있는 파일 노드는 같은 그룹의 다른 파일 노드와 같은 리프 블록을 참조하는 것이 좋으며, link-group과 comment를 제외하고 그 파일 노드들과 같은 엔트리 추가 정보를 가지는 것이 좋습니다.
int로 인코딩할 수 있으므로 inode 번호(st_ino)를 그대로 사용할 수 있게 해 주며, 이전 세그먼트를 읽지 않고도 할당할 수 있습니다. 그래도 inode 번호는 단일 파일 시스템 안에서만 고유하므로, 여러 파일 시스템에서 읽어 들이는 압축기는 그중 일부의 번호를 다시 매겨야 합니다. 아카이브 전역 그룹은 세그먼트를 넘나들며 파일을 연결하며, 예를 들어 압축기가 단일 트리를 아카이브하는 동안 주기적으로 새 세그먼트를 시작하는 경우나 기존 아카이브에 세그먼트를 덧붙이는 경우에 사용됩니다. 아카이브 전역 그룹을 사용하는 압축기는 예를 들어 덧붙일 때 이전 메타데이터 블록을 읽는 방식 등으로 그 그룹을 다른 모든 세그먼트와 일관되게 유지할 책임이 있습니다. inode 번호는 파일이 제거된 후 재사용되거나 관련 없는 파일 시스템에서 올 수 있으므로 그대로 아카이브 전역 값으로 사용하기에 적합하지 않으며, 내용이 같은 관련 없는 파일들을 연결하는 것은 어떤 압축 해제기도 감지할 수 없습니다.
최종 아카이브 값에서 해당 그룹을 가진 모든 파일 노드가 같은 내용을 가지면 그 링크 그룹은 일관됩니다. 예를 들어 이후 세그먼트가 그 그룹의 파일 노드 중 일부만 대체한 경우처럼 일관되지 않은 그룹은 잘못된 아카이브 실패가 아니지만, 추출(§ 12 추출)에는 아무런 영향을 주지 않습니다.
link-group을 무시하는 압축 해제기도 같은 내용을 가진 올바른 파일들을 추출합니다. 일부 macOS Time Machine 볼륨에서 볼 수 있는 디렉터리 하드 링크는 표현하지 않습니다.
11.4. Windows 전용 메타데이터
aqr-entry-windows = (
? &(win-link-type: -60) => &(file: 0, directory: 1, junction: 2) ; Link nodes only
; TODO: file attributes (e.g. ZIP external file attributes)
)
win-link-type은 Windows에서 생성될 링크의 종류를 결정합니다(§ 12 추출). 이것은 링크 노드 이외의 노드에는 아무런 영향을 주지 않으며, 압축기는 그러한 노드에 이것을 부여하지 않는 것이 좋습니다. "file"과 "directory"는 각각 파일에 대한 심볼릭 링크와 디렉터리에 대한 심볼릭 링크를 나타내며, "junction"은 정션(마운트 포인트 리파스 포인트)을 나타냅니다.
11.5. Unix 전용 메타데이터
aqr-entry-unix = (
? &(unix-mode: -70) => uint .le 4095 ; Permission bits only (0o7777), without S_IFMT
? &(unix-uid: -71) => uint
? &(unix-gid: -72) => uint
)
unix-mode는 set-user-ID, set-group-ID, sticky 비트를 포함한 권한 비트만을 담습니다. 엔트리의 종류는 오직 그 노드(§ 4.1 아카이브 모델)에 의해서만 결정되므로, unix-mode는 파일 타입 비트(S_IFMT)를 결코 포함하지 않습니다.
unix-uid와 unix-gid는 소유자의 숫자 사용자 ID와 그룹 ID입니다.
ZIP에서 온 Unix 메타데이터는 § 16.4 ZIP의 Unix 메타데이터에서 다룹니다.
11.6. zip-extras: ZIP 추가 필드
zip-extras는 엔트리가 변환되어 온 원래 ZIP 엔트리의 추가 필드[APPNOTE]가 이 명세에서 달리 표현할 방법이 없는 파일 정보를 담고 있을 때, 그 추가 필드를 보존합니다.
aqr-entry-zip= ( ? &(zip-extras: -100) => aqr-zip-extras )aqr-zip-extras= { * aqr-zip-extra-key => bstr .size (0..65531) }aqr-zip-extra-key= 0..65535 / ; Header ID n in the local header -65536..-1 ; Header ID n in the central header, as -1 - n ; TODO: multiple extra fields with the same header ID
0부터 65535까지의 키 n은 로컬 헤더에서 헤더 ID가 n인 추가 필드를 나타내고, −65536부터 −1까지의 키 −1 − n은 중앙 헤더에서 헤더 ID가 n인 추가 필드를 나타냅니다. 값은 헤더 ID와 데이터 크기로 이루어진 4바이트 헤더를 제외한 추가 필드의 데이터입니다. 따라서 음이 아닌 키를 가진 값은 항상 로컬 헤더용 형식이며, 음수 키를 가진 값은 항상 중앙 헤더용 형식입니다.
ZIP 아카이브를 변환하는 압축기는 다음 요구 사항에 따라 엔트리의 어떤 추가 필드든 zip-extras에 넣을 수 있습니다.
-
같은 헤더 ID를 가진 로컬 헤더 버전이 바이트 단위로 동일하다면, 압축기는 추가 필드의 중앙 헤더 버전을 넣어서는 안 됩니다. 로컬 헤더 버전이 없는 중앙 헤더 버전은 음수 키로만 넣습니다.
-
압축기는 그 정보가 다른 곳, 즉 엔트리 이름, 아카이브 모델(§ 4.1 아카이브 모델) 또는
aqr-entry-extra의 다른 키에 완전히 표현되는 추가 필드를 넣어서는 안 됩니다. -
압축기는 크기, 오프셋, 패딩, 정렬, ZIP 아카이브에 대한 암호화와 서명처럼 ZIP 컨테이너 자체에만 의미가 있는 추가 필드를 넣어서는 안 됩니다.
-
압축기는 ZIP에 대응하는 것이 없는 루트 디렉터리에
zip-extras를 부여하지 않는 것이 좋습니다.
이러한 요구 사항의 적용을 받는 것으로 알려진 추가 필드는 § 16.5 보존하지 않는 추가 필드에 나열되어 있습니다. 등록되지 않은 헤더 ID를 가진 것을 포함하여 그 밖의 추가 필드는 있는 그대로 zip-extras에 넣을 수 있습니다.
zip-extras는 결코 권위 있는 정보가 아닙니다. 보존된 추가 필드의 정보가 아카이브 모델 또는 aqr-entry-extra의 다른 키와 일치하지 않는 경우, 후자를 사용해야 합니다. 압축기가 넣도록 허용되지 않은 추가 필드를 발견한 압축 해제기는 해당 필드만 무시하는 것이 좋으며, 아카이브 전체를 유효하지 않은 것으로 취급하지 않는 것이 좋습니다.
zip-extras는 ZIP으로부터의 단방향 변환을 위해 설계되었으며, 변환된 아카이브로부터 원래 ZIP 아카이브를 재구성하는 것은 목표가 아닙니다. 추가 필드를 보존하는 것은 플랫폼 전용 속성, 접근 제어 목록, 리소스 포크처럼 아직 고유한 키가 없는 정보를, 전용 키가 정의되었을 때 이미 변환된 아카이브로부터 복구할 수 있도록 하기 위함입니다. 알 수 없는 추가 필드도 같은 이유로 보존되며, 그중 일부는 ZIP 컨테이너에만 의미가 있을 수도 있습니다. 같은 헤더 ID의 로컬 헤더 버전과 중앙 헤더 버전은 형식이 서로 다른 경우가 많습니다. 예를 들어 0x5455의 중앙 헤더 버전은 수정 시각만 가지며, 많은 Info-ZIP 속성 블록의 중앙 헤더 버전은 요약만 가집니다. 키로 이들을 구별하면 소비자가 각 값을 올바른 형식으로 해석할 수 있습니다. 중앙 헤더 버전의 키가 −n이 아니라 −1 − n인 것은 헤더 ID 0이 로컬 헤더 버전과 충돌하지 않도록 하고, 두 버전이 CBOR에서 같은 인코딩 길이를 갖도록 하기 위함입니다.
12. 추출
아카이브 값을 파일 시스템에 쓰는 것은 아카이브 모델의 범위 밖입니다. 이를 수행하는 압축 해제기는 다음 요구 사항을 따릅니다.
-
대상 디렉터리에 이미 존재하거나 추출 중에 생성된 심볼릭 링크 또는 기타 리디렉션을 통하는 경우를 포함하여, 대상 디렉터리 밖의 어떤 파일도 생성, 수정 또는 삭제해서는 안 됩니다.
-
압축 해제기는 인코딩이 "
wtf-8"인 엔트리 이름을 플랫폼 고유의 유니코드 이름(예: Windows에서는 짝이 없는 서로게이트를 포함할 수 있는 UTF-16)으로 매핑하고, 인코딩이 "raw"인 엔트리 이름을 플랫폼이 허용하는 경우 동일한 바이트열로 매핑하는 것이 좋습니다. -
엔트리 이름을 플랫폼에서 표현할 수 없거나 그 이름이 플랫폼에서 특별한 의미를 갖는 경우(예:
\또는:를 포함하는 이름, 또는 Windows의 예약된 장치 이름), 실패하거나 대체된 이름을 사용자에게 보고해야 하며, 아무 알림 없이 다른 이름을 사용해서는 안 됩니다. -
한 디렉터리 안의 여러 엔트리 이름이 같은 파일 시스템 이름으로 매핑되는 경우(예: 대소문자 비구분이나 유니코드 정규화로 인해), 실패하거나 충돌을 보고해야 하며, 나중 엔트리를 앞선 엔트리를 통해 써서는 안 됩니다.
-
모든 노드의 extra(예: extra 및 extra)를 무시할 수 있습니다. 명시적으로 요청되지 않는 한
unix-uid및unix-gid와 같은 소유권 정보를 적용하지 않는 것이 좋습니다.
A와 파일 a/x가 같은 디렉터리로 매핑되는 경우가 그렇습니다. 위의 이름 충돌 규칙이 이미 이를 방지하며, 첫 번째 요구 사항이 이를 명시적으로 규정합니다. 가능한 구현 방법으로는 다른 모든 노드를 생성한 후에 모든 링크를 생성하거나, 링크를 따라가지 않고 각 경로를 부모 디렉터리에 대해 상대적으로 여는 방법(예: O_NOFOLLOW를 사용한 openat, 또는 Linux의 RESOLVE_BENEATH)이 있습니다.
링크 노드를 추출하는 압축 해제기는 다음의 추가 요구 사항을 따릅니다.
-
명시적으로 요청되지 않는 한 대상이 대상 디렉터리를 벗어나는 링크를 생성해서는 안 되며, 이를 요청하는 방법을 제공하는 것이 좋습니다. 링크 대상이 절대 경로이거나 플랫폼의 드라이브 또는 루트에 대한 상대 경로인 경우, 또는 링크의 부모 디렉터리에서부터 그 경로를 해석하면서 도중에 아카이브 값 안의 링크 노드를 따라갔을 때 대상 디렉터리의 상위 디렉터리에 도달하는 경우, 그 링크 대상은 대상 디렉터리를 벗어납니다. 이러한 이유로 생성되지 않은 링크는 사용자에게 보고해야 합니다.
그러한 링크를 생성하는 것 자체가 대상 디렉터리 밖에 쓰는 것은 아니지만, 추출된 트리로 인해 이후의 사용자나 프로그램이 그렇게 하게 될 수 있습니다. 대상을 어휘적으로 해석하는 것만으로는 충분하지 않습니다.d/e/l이../../q에 대한 링크일 때,d/e/l/../..에 대한 링크는 어휘적으로는d로 해석되지만 실제로는 대상 디렉터리의 상위 디렉터리에 도달합니다. -
Windows에서는 링크 대상의 모든
/를\로 바꾸는 것이 좋습니다.win-link-type이 있다면 그것이 지정하는 종류의 링크를 생성하는 것이 좋습니다. 그렇지 않으면, 위와 같이 대상이 아카이브 값 안의 디렉터리 노드로 해석되는 경우 디렉터리에 대한 심볼릭 링크를, 그 외의 경우 파일에 대한 심볼릭 링크를 생성하는 것이 좋습니다. -
권한 부족, 파일 시스템, 또는 대상의 길이 등으로 인해 링크를 생성할 수 없는 경우, 실패하거나 이를 사용자에게 보고해야 하며, 아무 알림 없이 다른 종류의 노드를 생성해서는 안 됩니다. 이를 보고한다면 대신 대상의 사본을 생성할 수 있습니다.
-
링크 노드의 extra를 그 대상에 적용해서는 안 됩니다. 타임스탬프, 소유권 및 권한은 플랫폼이 허용하는 경우 링크 자체에 적용하는 것이 좋습니다(예:
lutimes,lchown).
압축 해제기는 같은 일관된 링크 그룹을 가진 파일 노드 사이에 하드 링크를 생성할 수 있습니다. 그 밖의 파일 노드 사이에는, 내용이 같더라도 하드 링크를 생성해서는 안 됩니다.
rsync --link-dest로 만든 스냅숏 백업에 매우 중요합니다. 하지만 독립적이었던 파일은 독립적으로 남아 있어야 합니다.
압축 해제기는 소유권 정보와 마찬가지로, 명시적으로 요청되지 않는 한 특수 노드에 대해 FIFO나 장치 파일을 생성하지 않는 것이 좋습니다. 이러한 이유로, 또는 플랫폼이 표현할 수 없다는 이유로(예: 권한 부족, 또는 플랫폼의 범위를 벗어난 장치 번호) 생성되지 않은 특수 노드는 사용자에게 보고해야 합니다.
압축 해제기는 미지 노드에 대해 아무것도 생성해서는 안 되며, 이를 사용자에게 보고해야 합니다. 압축 해제기는 다른 노드의 추출을 계속할 수 있습니다.
13. 적합성
비규범적이라고 표시된 절뿐만 아니라, 이 명세의 모든 다이어그램, 예시, 참고는 비규범적입니다. 이 명세의 그 밖의 모든 내용은 규범적입니다.
이 명세의 핵심어 MAY, MUST, MUST NOT, RECOMMENDED, SHOULD, SHOULD NOT은 여기에 표시된 것처럼 모두 대문자로 나타날 때, 그리고 그때에만 BCP 14 [RFC2119] [RFC8174]에 설명된 대로 해석됩니다.
13.1. 적합성 수준
적합한 압축 해제기는 자신이 적용하는 모듈(§ 7 모듈)에 따라 다음 중 하나의 적합성 수준을 가집니다:
- 레벨 0
-
어떤 모듈도 적용하지 않습니다.
- 레벨 1
-
모든 등록 모듈, 즉 모듈 해시가 Aqueous 모듈 레지스트리(§ 15.6 Aqueous 모듈 레지스트리)의 한 엔트리의 등록 해시인 모든 모듈을 적용하며, 그 밖의 모듈은 적용하지 않습니다.
- 레벨 2
-
모든 모듈을 적용합니다.
수준 0 또는 수준 1의 압축 해제기는 자신의 수준에 포함되지 않는 모듈의 적용을 거부할 수 있으며, 이 경우 리소스 실패가 발생합니다. 수준 2의 압축 해제기는 모듈의 적용을 거부해서는 안 되지만, 모듈이 자신의 한도를 초과하면 여전히 리소스 실패로 실패할 수도 있습니다. 수준은 압축 해제기에 대한 그 밖의 어떤 요구 사항도 바꾸지 않습니다. 예를 들어 수준 0의 압축 해제기도 Apply 명령이 없는 공식 블록은 여전히 읽습니다.
13.2. 부분 접근
압축 해제기는 엔트리의 부분 집합, 콘텐츠의 바이트 범위, 또는 콘텐츠 없는 엔트리 이름과 같이 아카이브 해석 결과의 일부만 계산할 수 있습니다. 전체 결과가 아카이브 값이라면, 그러한 부분 결과는 전체 결과의 해당 부분과 같아야 합니다.
부분 결과를 계산하는 압축 해제기는 자신이 계산하지 않는 부분만으로 인해 발생하는 잘못된 아카이브 실패를 감지하지 않아도 됩니다. 압축 해제기는 자신이 계산한 바이트만을 대상으로 하는 모든 체크섬을 검증하는 것이 좋습니다.
14. 보안 고려 사항
아카이브는 임의의 데이터를 담는 컨테이너이며, 압축 해제기가 파일 내용을 재구성하기 위해 실행하는 WebAssembly 모듈(§ 7.3 모듈 실행)을 제외하면 그 자체로 능동적 콘텐츠를 포함하지 않습니다. 이러한 모듈은 입력과 출력 이외에는 아무것에도 접근할 수 없는 결정적 프로필에서 실행되지만, 압축 해제기는 그 모듈 해시(§ 7.1 모듈 블록)를 알고 있지 않는 한 여전히 이를 신뢰할 수 없는 코드로 취급하고 그에 맞게 격리해야 합니다. 공식 블록과 모듈은 아카이브 자체보다 훨씬 큰 출력을 생성할 수 있으며, 일부 필드(예: lg_work_factor)는 계산량을 직접 제어합니다. 압축 해제기는 구현 정의 한도를 적용하고, 그 한도를 초과하면 리소스 실패로 실패할 것으로 기대됩니다. 아카이브를 파일 시스템에 기록하는 압축 해제기는 § 12 추출의 요구 사항을 따라야 하며, 특히 대상 디렉터리 바깥에 기록하는 것을 방지해야 합니다. 암호화되지 않은 아카이브는 기밀성을 제공하지 않으며, 그 XXH64 체크섬(§ 10.1 체크섬)은 우발적인 손상만 탐지할 뿐 의도적인 변조는 탐지하지 못합니다. 암호화 아카이브(§ 9 암호화 아카이브)는 [AGE]를 통해 기밀성과 무결성을 제공하며, 그 강도는 사용된 패스프레이즈의 강도에 달려 있습니다. 엔트리 이름, 타임스탬프, 소유권을 포함한 아카이브의 메타데이터는 해당 아카이브를 생성한 시스템에 대한 정보를 드러낼 수 있습니다.
오버레이 아카이브는 베이스(일반적으로 파일 시스템의 디렉터리)를 기준으로 해석되며, 베이스의 어떤 부분이든 그 결과에 복사할 수 있습니다. 베이스를 읽는 압축 해제기는 § 8.3 베이스 읽기의 요구 사항을 따라야 하며, 특히 베이스가 명시적으로 주어진 경우에만 베이스를 사용하고 절대 그 바깥을 읽지 않아야 합니다. 오버레이 아카이브를 제자리에서 적용하면 베이스 디렉터리의 파일이 수정되고 삭제됩니다.
15. IANA 고려 사항
15.1.
application/aqr 미디어 타입
이 문서는 다음과 같이 Media Types Registry([RFC6838], 5.6절)에 새로운 미디어 타입의 등록을 요청합니다.
- 타입 이름
- application
- 서브타입 이름
- aqr
- 필수 매개변수
- 해당 없음
- 선택 매개변수
- 해당 없음
- 인코딩 고려 사항
- 바이너리
- 보안 고려 사항
- § 14 보안 고려 사항를 참조하십시오.
- 상호 운용성 고려 사항
-
암호화되지 않은 아카이브의 앞과 뒤에는 임의의 바이트(예: 자동 압축 해제 실행 파일 스텁)가 올 수 있으며, 아카이브의 범위는 header_magic과 푸터(§ 3.6 아카이브 찾기)로 결정됩니다. 메타데이터 블록이 하나도 없는 아카이브는 디렉터리 트리가 아니라 이름 없는 단일 파일로 압축 해제됩니다. 오버레이 아카이브는 자체 미디어 타입
application/aqo(§ 15.2 application/aqo 미디어 타입)를 가지며, 페이로드가 오버레이 아카이브인 암호화 아카이브도 해당 타입입니다. - 공개된 명세
- 이 문서
- 이 미디어 타입을 사용하는 애플리케이션
- Aqueous 아카이브 형식을 구현하는 아카이버 및 압축기.
- 프래그먼트 식별자 고려 사항
- 해당 없음
- 추가 정보
-
- 이 타입의 폐기된 별칭
- 해당 없음
- 매직 넘버
- 암호화되지 않은 아카이브의 경우 F8 04 03 6A D6 BE(16진수, HEADER_MAGIC)이며, 그 앞에 다른 데이터가 올 수 있습니다. 암호화 아카이브의 경우 오프셋 0에 F9 CA F2 A6 D0 2A(16진수, ENCRYPTED_HEADER_MAGIC와 ENCRYPTED_MIRRORED_HEADER_MAGIC의 공통 접두사)가 옵니다.
- 파일 확장자
- .aqr
단일 파일을 메타데이터 블록 없이 아카이브로 압축하는 경우, 대신 원래 파일 이름에 확장자를 덧붙일 수 있습니다(예:example.tar.aqr). - Macintosh 파일 타입 코드
- 해당 없음
- 추가 정보를 위한 연락 담당자 및 이메일 주소
- Kang Seonghoon <public+aqr@mearie.org>
- 사용 목적
- COMMON
- 사용 제한
- 해당 없음
- 작성자
- Kang Seonghoon
- 변경 관리자
- Kang Seonghoon
15.2.
application/aqo 미디어 타입
이 문서는 미디어 타입 레지스트리([RFC6838], 5.6절)에 다음과 같이 새 미디어 타입의 등록을 요청합니다.
- 타입 이름
- application
- 서브타입 이름
- aqo
- 필수 매개변수
- 해당 없음
- 선택 매개변수
- 해당 없음
- 인코딩 고려 사항
- 바이너리
- 보안 고려 사항
- § 14 보안 고려 사항를 참조하십시오.
- 상호 운용성 고려 사항
-
오버레이 아카이브는 베이스 없이는 해석할 수 없습니다. 오버레이 아카이브의 매직 넘버는
application/aqr의 매직 넘버와 다르므로, 오버레이 아카이브를 지원하지 않는 압축 해제기가 이를 독립 아카이브로 오인하지 않습니다. 그 밖에는application/aqr의 상호 운용성 고려 사항이 적용됩니다. - 공개된 명세
- 이 문서
- 이 미디어 타입을 사용하는 애플리케이션
- Aqueous 오버레이 아카이브 형식을 구현하는 아카이버, 패치 도구 및 업데이터입니다.
- 프래그먼트 식별자 고려 사항
- 해당 없음
- 추가 정보
-
- 이 타입의 폐기된 별칭 이름
- 해당 없음
- 매직 넘버
- 암호화되지 않은 오버레이 아카이브의 경우 69 0D 15 AF F6 B2(16진수; OVERLAY_HEADER_MAGIC)이며, 그 앞에 다른 데이터가 올 수 있습니다. 암호화된 오버레이 아카이브는
application/aqr의 암호화 아카이브와 같은 매직 넘버를 가집니다. - 파일 확장자
- .aqo
- Macintosh 파일 타입 코드
- 해당 없음
- 추가 정보를 위한 연락처 담당자 및 이메일 주소
- Kang Seonghoon <public+aqr@mearie.org>
- 의도된 용도
- COMMON
- 사용 제한
- 해당 없음
- 작성자
- Kang Seonghoon
- 변경 관리자
- Kang Seonghoon
15.3.
+aqr 구조화된 구문 접미사
이 문서는 구조화된 구문 접미사 레지스트리([RFC6838], 6.2절)에 다음과 같이 새로운 구조화된 구문 접미사의 등록을 요청합니다.
- 이름
- Aqueous 아카이브 형식
- +suffix
- +aqr
- 참고 문헌
- 이 문서.
- 인코딩 고려 사항
- 바이너리
- 상호 운용성 고려 사항
-
+aqr접미사를 가진 미디어 타입은 암호화되지 않았거나 암호화된 독립 아카이브를 나타내며, 그 아카이브 값에는 해당 미디어 타입에 의해 보다 구체적인 의미가 부여됩니다. 일반적인 Aqueous 압축 해제기는 그 의미를 알지 못하더라도 그러한 미디어 타입의 아카이브를 해석할 수 있습니다.application/aqr(§ 15.1 application/aqr 미디어 타입)에 대한 상호 운용성 고려 사항도 적용됩니다. - 프래그먼트 식별자 고려 사항
-
+aqr에 대해 지정된 프래그먼트 식별자의 구문과 의미는application/aqr에 대해 지정된 것과 같은 것이 좋습니다. (이 문서의 발행 시점에는application/aqr에 대해 정의된 프래그먼트 식별 구문이 없습니다.)특정
xxx/yyy+aqr에 대한 프래그먼트 식별자의 구문과 의미는 다음과 같이 처리하는 것이 좋습니다.+aqr에 정의된 경우로서 프래그먼트 식별자가+aqr규칙에 따라 해석되는 경우에는+aqr에 지정된 대로 처리합니다.+aqr에 정의된 경우로서 프래그먼트 식별자가+aqr규칙에 따라 해석되지 않는 경우에는xxx/yyy+aqr에 지정된 대로 처리합니다.+aqr에 정의되지 않은 경우에는xxx/yyy+aqr에 지정된 대로 처리합니다.
- 보안 고려 사항
- § 14 보안 고려 사항의 보안 고려 사항은
+aqr접미사를 가진 모든 미디어 타입에 적용됩니다. 그러한 각 미디어 타입에는 아카이브에 대한 해당 미디어 타입의 해석에 고유한 추가 보안 고려 사항이 있을 수 있습니다. - 연락처
- Kang Seonghoon <public+aqr@mearie.org>
- 작성자/변경 관리자
- Kang Seonghoon
15.4. 양자화 간격
이 문서는 Time Tag Map Keys Registry([RFC9581], 섹션 7.3)에 다음과 같이 새 키를 등록할 것을 요청합니다:
- 키 이름
- 양자화 간격
- 키 값
- -14 (잠정)
- 참조
- 이 문서의 양자화 간격 절
- 의미
-
양자화 간격은 주어진 시간 값을 기준으로 실제 시간이 놓인 구간을 지정합니다. 즉, 시간 값이 t이고 양자화 간격이 a부터 b까지라면, 실제 시간은 t + a 이상 t + b 이하의 범위에 있습니다. 양자화란 실제 시간 값을 이산적인 표현으로 절사하거나 반올림하는 모든 수단을 말하며, 특히 기반 시계의 정확도와는 관련이 없습니다. 예를 들어, 시계의 정밀도는 높지만 시간 값이 초 단위로 절삭되어 있을 수 있으며, 이 경우 양자화 간격은 [0, 1초]가 됩니다.
구간은 항상 닫힌 구간입니다. 구간은 가능한 실제 시간의 정확한 집합보다 약간 더 넓을 수 있지만(예: 절사의 경우 [0, 1초]이며, 이때 실제 시간은 결코 정확히 1초 뒤일 수 없습니다), 반올림에서 동점인 경우를 포함하여 가능한 실제 시간을 결코 배제하지 않습니다.
양자화 간격은 다음과 같은 방식으로 주어질 수 있습니다.
- 초 단위의 숫자 값 또는 기간 y이며, [0, y] 구간을 나타냅니다.
- 각각이 초 단위의 숫자 값 또는 기간인 두 요소의 배열
[x, y]이며, [−x, y] 구간을 나타냅니다. 즉, x는 시간 값 이전의 범위이고 y는 시간 값 이후의 범위입니다.
주어진 시간 값이 항상 구간에 포함되도록, 모든 숫자 값은 유한하고 음수가 아니어야 하며, 모든 기간은 음수가 아닌 키 1을 가져야 합니다. 구간은 단일 점이어서는 안 됩니다. 따라서 단일 숫자 값 또는 기간은 0이어서는 안 되며, 배열
[x, y]는 x와 y가 모두 0이어서는 안 됩니다. x가 0이면 배열을 사용해서는 안 되며, 대신 y만 단독으로 주어야 합니다.확장 시간에 이 키가 없으면 그 양자화 간격은 알 수 없습니다. 특히, 시간 값이 정확하다는 의미를 함축하지 않습니다.
예를 들어 다음 중 처음 두 개는 10밀리초 단위로 절사된 시간 값을 나타내고, 마지막 하나는 가장 가까운 초로 반올림된 시간 값을 나타냅니다:
1001({1: 1697724754, -3: 870, -14: 0.01}), 1001({1: 1697724754, -3: 870, -14: {1: 0, -3: 10}}), 1001({1: 1697724754, -14: [0.5, 0.5]})
다음 CDDL 선언을 사용하여 양자화 간격 키를 나타낼 수 있습니다.
quantization-interval = ~time / ~duration / [ 2*2 ~time/~duration ]
$$ETIME-ELECTIVE //= (-14: quantization-interval)
15.5. Aqueous 사전 레지스트리
이 문서는 IANA에 "Aqueous 사전"이라는 제목의 새 레지스트리를 만들어 줄 것을 요청하며, 이 레지스트리에는 유형 5016의 약칭 블록이 참조할 수 있는 등록 사전이 등재됩니다(§ 4.2.2 등록 사전과 등록 모듈). 새 엔트리는 Specification Required 정책([RFC8126], 섹션 4.6)에 따라 할당됩니다. 각 엔트리는 다음 필드를 가집니다:
- 해시
- 등록 해시, 즉 사전 바이트의 SHA-512/256 해시 [FIPS180-4]이며, 64개의 소문자 16진수 숫자로 표기합니다.
- 이름
- 사람이 알아보기 위한 사전의 짧은 이름이며, 형식에는 아무런 영향을 주지 않습니다.
- 엔트로피 테이블
- 사전에 엔트로피 테이블이 있는지 여부, 즉 has_entropy_tables의 값입니다.
- 데이터
- 사전의 바이트로, 디코딩된 데이터이며 § 5.2 사전 블록에 따른 사전 블록의 데이터입니다. 이 데이터는 사전 블록의 데이터로서 유효해야 하며 등록 시 함께 제출해야 합니다.
- 참조
- 사전과 그 용도를 설명하는 문서입니다.
지정 전문가는 해시가 데이터와 일치하는지, 그리고 같은 해시를 가진 엔트리가 없는지 확인합니다. 아카이브는 엔트리를 해시로만 참조하므로, 한 번 등록된 엔트리는 수정하거나 삭제해서는 안 됩니다. 엔트리는 데이터가 같더라도 Aqueous 모듈 레지스트리(§ 15.6 Aqueous 모듈 레지스트리)와 절대 공유되지 않습니다.
레지스트리의 초기 내용은 다음과 같습니다.
| 해시 | 이름 | 엔트로피 테이블 | 참조 |
|---|---|---|---|
| TBD | text | TBD | 이 문서 |
| TBD | modules | TBD | 이 문서 |
사전 "text"는 일반 텍스트를 위한 것이고, 사전 "modules"는 전체가 주어지는 모듈 블록(§ 7.1 모듈 블록)을 위한 것입니다.
초기 사전의 데이터와 해시는 아직 만들어지지 않았습니다.
15.6. Aqueous 모듈 레지스트리
이 문서는 IANA에 "Aqueous 모듈"이라는 제목의 새 레지스트리를 만들어 줄 것을 요청하며, 이 레지스트리에는 유형 6016의 약칭 블록이 참조할 수 있는 등록 모듈이 등재됩니다(§ 4.2.2 등록 사전과 등록 모듈). 새 엔트리는 Specification Required 정책([RFC8126], 4.6절)에 따라 할당됩니다. 각 엔트리에는 다음 필드가 있습니다.
- 해시
- 등록 해시, 즉 모듈의 모듈 해시로, 64개의 소문자 16진수 숫자로 기재합니다.
- 이름
- 사람이 읽기 위한 모듈의 짧은 이름으로, 형식에는 아무런 영향을 주지 않으며, 모듈의 이름과 버전을
@로 연결하여 기재합니다(예:flate-set@1). 한 모듈의 서로 다른 버전은@앞의 이름이 같고 해시가 다릅니다. - 데이터
- 모듈 블록(§ 7.1 모듈 블록)의 wasm_module에 해당하는 모듈의 바이트로, 그러한 것으로서 유효해야 하며 등록 시 함께 제출되어야 합니다.
- 참조
- 모듈이 역변환하는 변환과 그 입력, 리소스, 매개변수를 설명하는 문서이며, 가급적 모듈을 빌드한 소스 코드도 포함합니다.
지정 전문가는 해시가 데이터와 일치하는지, 같은 해시를 가진 엔트리가 없는지, 그리고 모듈이 § 7.3 모듈 실행을 준수하는지 확인합니다. 아카이브는 엔트리를 해시로만 참조하므로, 한 번 등록된 엔트리는 수정되거나 제거되어서는 안 됩니다. 엔트리는 두 레지스트리의 데이터가 같더라도 Aqueous 사전 레지스트리(§ 15.5 Aqueous 사전 레지스트리)와 결코 공유되지 않습니다.
레지스트리의 초기 내용은 이 문서의 참조 구현이 빌드한 모듈이며, 각 엔트리의 데이터(D)와 참조(R)를 연결하여 다음과 같습니다.
| 해시 | 이름 | 링크 |
|---|---|---|
caca82f410e08aec7048aa663fb5fc4d376394b44b4ad89f4373dd8053427f1c
| bcn-recons@1 | D R |
30ebd39ea3f768f06cf4d3e79872666d6923eb8e608ed5878bcc86c040179a21
| bundle-recons@1 | D R |
8531516a33c8327cd7423dc838e88edcb16d520f1a621f2b47eda75f27de8922
| clip-recons@1 | D R |
6a9359342d4e3712baeb3d615ce0bc39a3283412da430f4c3098e2a71560e48d
| exe-recons/arm64@1 | D R |
7b22bbac03ed25bd765b8d27207f6ddd9d14bd4a683b3046e27af801202f4d2a
| exe-recons/cil@1 | D R |
5cc8b375caa7adbeadd5ec65845c6996070f3bab79e6ed5dc4ad8c62756eb60b
| exe-recons/pe-reloc@1 | D R |
7d11c15482190e15bdaef0b1206151ea75bf0aad3092136e6c25e4f8931ca257
| exe-recons/x86@3 | D R |
f15b8d3355fc8929c25d263b6b752516a3e675cbd476ee5933f6900a8cb9718b
| fasta-recons@1 | D R |
3944fa75e9f3991a741c305f7e410cfdb2b73b17f57691da9de94cd142fbf968
| fits-recons-image@1 | D R |
4433f4e59eca939357f8d3c5821b7ba887b38f27c7c9c438dedcfb40bcf6b796
| fits-recons-rice@1 | D R |
c25884adc026793bca3729098d4adc0d10cc9015b5094e9b2a2d2043d10bc80f
| fits-recons-table@1 | D R |
568697ef4a3d656873eaf79124c4910134f1fd5c27368cff151f4be592eae8bd
| flac-recons@3 | D R |
9d3e8b19692ad0949412eeb156b0ababceb2c032b9bb90ee2ff33b4788fbdf7c
| flate-set@1 | D R |
17849cd2f570cd4ff266e6c7c7e7656954fff0b7b53a032682e630874b1d27e3
| git-pack-recons@1 | D R |
04372842501e11c7b00cb9318413f5e2e60b208b4b51a316514d22c16491784a
| gnu-gettext-recons@1 | D R |
8cac476b42906e38a2356829e851368e0f50d7a9e19fcaf1a5c1f37ea53a0f81
| ima-recons@2 | D R |
211a60941bbf02f93c8631eac928329c5df633aeea60a60dce24bea161a537e6
| jpeg-recons@2 | D R |
987cf097113c1744fc5c4152d2c2a0690f9bf7619050ecdf83dde7194e3dd1a7
| leveldb-recons@1 | D R |
fd1cad1d213fa4842374deb2b699034867b3de8133ab6b2595a5f5653f9f2368
| lz4-recons@1 | D R |
1585d87b3e78ab476c07e3cbb7294a8ab8957221996db7e56641e7958352ff7f
| lzma-recons@1 | D R |
88d1abe44c2ff42ecaffa3b983ba7271d67d6a0015d67621bfde47589bbecb5c
| lzo-recons@1 | D R |
57a624b911608583a899696341843976301146b0b0da8e1e9bdcb2e89738c47a
| mp3-recons@5 | D R |
7802fa7cd30c778f5a28f6928c14e4facd2f7bfa16d0559f8f3d9001625b78d3
| obj-recons@1 | D R |
94de97052f0648d4a46838cff795de9fa50d54f0fed1d9626d4468daefddb91b
| pcm-recons@1 | D R |
59b6c21e5ce4e15f567b6af7af919ce474ecfdfe0317b1c5a24a2ebd1069a428
| pdf-recons@2 | D R |
874cc85df19af27651e5b9904298a068eda45f331778958f84dbd8966c42bb26
| png-against@1 | D R |
ff7fddf83d8f02aed7d9e00dfcd6536c657674093e56f235526a7e15a632ddd1
| png-recons@3 | D R |
e9255925acd09473d6c3456e9213635af299edbe66aef6ef21f065d31a504884
| png-set@2 | D R |
0e04bbf105c680af27c18865587bd24b3ae2b48e7481cb8cb1df130296b641df
| psd-recons@1 | D R |
cb0f12a5a24d451e9c2ba996d47f3f5f6586d6b3ec9ec0e842a52f4bf1e4f3cc
| raster-recons-16@1 | D R |
0f77bdf94f1fd11542160d25cc26737c01b68c5f557c885d0cda0a88e092738e
| raster-recons@1 | D R |
df80c12c9ad47bc6cd4f66d39a7ac02e124777a1a5a3a3e84eba79d569864f31
| recs-entries@1 | D R |
03fae1c3201f91c5b8512b43d0d261a19ab5568f280233df170b7026e90e5275
| recs-fields@1 | D R |
672c1f43cc0d93e5bd5dc733f7fa890ff37bb3b796c338198a21ed67b5bbe14d
| snappy-recons@2 | D R |
fdfd0dfefc7da381bfd33e7e5a4e338d08a11a88e68616c6077d279fe9072801
| sqlite-recons@1 | D R |
3ac92678e2aad46f7f9d12b9b951ec970b8e2ed039acc25f61e834d4410a0f80
| vorbis-recons@2 | D R |
46a1e89ed82d1585732dc8ac52d78d472618feb5ed715d9b943aa141dac1d4c2
| wrt-recons-hangul@1 | D R |
78114341a3981b9b877f74beff267c6fcb83f23e7106d344107fbc178dab3117
| wrt-recons@1 | D R |
928d51f81c0b917f2cd38519d76703eeaae67766d2d41e3f117204382ce6979f
| xsv-recons@1 | D R |
63150a666d6cb14bf9162a5f353343612b2cfc2a4111f5d4b2a15324598d91cc
| zip-recons@3 | D R |
초기 엔트리는 아직 확정되지 않았습니다. 이 엔트리의 모듈과 이를 설명하는 문서는 이 문서의 첫 번째 버전이 나오기 전까지 변경될 수 있으며, 그때까지 변경된 엔트리는 새 버전으로 추가되지 않고 이 표에서 대체됩니다.
16. ZIP에서 변환하기
이 절은 비규범적입니다.
이 절에서는 ZIP이 동일한 정보에 대해 여러 출처나 인코딩을 제공하는 경우에, 압축기가 ZIP 아카이브 [APPNOTE]의 엔트리를 정보 손실 없이 아카이브 값으로 변환하는 방법을 설명합니다.
16.1. ZIP의 엔트리 이름
ZIP 아카이브 [APPNOTE]는 각 엔트리의 경로를 로컬 헤더와 중앙 헤더에 바이트열로 저장하며, 구성 요소는 /로 구분됩니다. 그 인코딩은 헤더만으로 항상 알 수 있는 것은 아니며, 여러 추가 필드가 같은 경로의 유니코드 버전을 담고 있습니다. ZIP 아카이브를 변환하는 압축기는 다음 출처로부터 각 구성 요소의 인코딩을 결정할 수 있으며, 앞에 있는 출처가 우선합니다.
-
NameCRC32가 헤더에 있는 경로의 CRC-32와 일치하는 버전 1의 추가 필드 0x7075(Info-ZIP Unicode Path) [INFOZIP-EXTRAFLD]. 이 필드는 경로를 UTF-8로 담고 있습니다. CRC-32가 일치하지 않는 필드는 필드를 갱신하지 않고 경로만 변경한 도구가 남긴 것일 수 있으므로, [APPNOTE]에 명시된 대로 무시됩니다. -
범용 비트 플래그의 비트 11(언어 인코딩 플래그)이 설정된 경우, 헤더에 있는 경로입니다. 이 경로는 UTF-8입니다.
-
추가 필드 0x554e(Xceed 유니코드)는 경로를 UTF-16LE로 담습니다. 이는 WTF-8로 변환되며, WTF-8은 짝이 없는 서로게이트도 보존합니다.
-
같은 필드에 주어진 코드 페이지로 인코딩된 경로를 담고 있는 추가 필드 0x5a4c(ZipArchive code page). 이 필드에 코드 페이지만 있는 경우에는 대신 헤더에 있는 경로를 그 코드 페이지로 디코딩합니다.
-
헤더에 있는 경로의 코드 페이지를 알려 주는 추가 필드 0xe57a(ALZip code page).
-
그렇지 않으면 헤더에 있는 경로의 인코딩은 지정되어 있지 않습니다. 전통적으로는 IBM 코드 페이지 437이지만, 아카이브를 만든 기기의 시스템 코드 페이지인 경우가 많으므로 압축기는 인코딩을 추측해야 합니다. 추측할 수 없으면 각 구성 요소에 대해 (C116을 앞에 붙인) 원시 바이트열을 사용합니다.
16.2. ZIP의 타임스탬프
ZIP 아카이브 [APPNOTE]는 같은 타임스탬프를 여러 곳에 담을 수 있습니다. 로컬 헤더와 중앙 헤더의 DOS 날짜 및 시각, 그리고 추가 필드 0x000a(NTFS), 0x5455(extended timestamp), 0x5855(Info-ZIP UNIX type 1), 0x000d(UNIX) [INFOZIP-EXTRAFLD]가 그것입니다. ZIP 아카이브를 변환하는 압축기는 각 키의 출처를 다음과 같이 선택할 수 있으며, 앞에 있는 출처가 우선합니다. 이 순서는 더 정밀한 출처를 먼저 우선하고, 정밀도가 같으면 더 최근의 명세를 우선합니다.
| 키 | 출처 |
|---|---|
crtime
| 0x000a Ctime; 0x5455 CrTime(로컬 헤더에만 있음)
|
ctime
| - |
mtime
| 0x000a Mtime; 0x5455 ModTime; 0x5855 또는 0x000d ModTime; DOS 날짜 및 시각
|
atime
| 0x000a Atime; 0x5455 AcTime(로컬 헤더에만 있음); 0x5855 또는 0x000d AcTime
|
arctime
| - |
각 출처는 다음과 같이 인코딩됩니다.
-
0x000a는 1601-01-01T00:00:00Z 이후의 100 ns 간격 수인 Windows
FILETIMEt를 저장하며, 이는125(t − 116444736000000000)로 인코딩됩니다. 값이 0이면 타임스탬프가 없다는 뜻입니다. -
0x5455, 0x5855, 0x000d는 1970-01-01T00:00:00Z 이후의 초 수를 저장하며, 이는 태그 없는
int로 인코딩됩니다. [INFOZIP-EXTRAFLD]에 따르면 이는 부호 있는 32비트 정수이지만, 일부 구현은 2038년 이후의 시각을 부호 없는 값으로 기록합니다. 0x5455의 중앙 헤더 버전은 플래그가 다른 값을 나타내더라도ModTime만 가지므로, 로컬 헤더 버전이 있으면 그것을 우선합니다. -
DOS 날짜 및 시각은 알 수 없는 시간대의 지역 시간으로 2초의 정밀도를 가지며, 2n이 해당 지역 시간을 UTC로 간주한 값일 때
[121(n)]로 인코딩됩니다.
mtime에 대해 DOS 날짜 및 시각과 UTC 출처가 모두 있으면, 그 차이로 아카이브가 만들어진 곳의 시간대 오프셋을 알 수 있습니다. k가 aqr-tz-offset 범위의 정수일 때 DOS 시각에서 UTC 시각을 뺀 값이 900k초와 2초 미만으로 차이 나면, u를 UTC 출처의 인코딩이라 할 때 mtime을 [u, k]로 인코딩할 수 있습니다. 그렇지 않으면, 예를 들어 시계 오차나 일광 절약 시간 전환 때문이라면, DOS 날짜 및 시각은 그냥 버립니다.
같은 키에 대한 두 출처가 양자화 간격보다 크게 어긋나면 압축기는 이에 대해 경고할 수 있으며, 그래도 우선하는 출처를 사용합니다.
16.3. ZIP의 주석
ZIP 아카이브 [APPNOTE]는 각 엔트리의 주석을 중앙 헤더에만 저장하고, 아카이브 전체의 주석은 중앙 디렉터리 끝 레코드에 저장합니다. 둘 다 바이트열이며, 빈 주석은 주석이 없는 것과 다르지 않으므로 압축기는 빈 주석에 대해 comment 또는 archive-comment를 생략할 수 있습니다.
ZIP 아카이브를 변환하는 압축기는 다음 출처로부터 각 엔트리 주석의 인코딩을 결정할 수 있으며, 앞의 출처가 우선합니다.
-
ComCRC32가 헤더에 있는 주석의 CRC-32와 일치하는, 중앙 헤더에 있는 버전 1의 추가 필드 0x6375(Info-ZIP Unicode Comment) [INFOZIP-EXTRAFLD]. 이 필드는 주석을 UTF-8로 담습니다. CRC-32가 일치하지 않는 필드는 § 16.1 ZIP의 엔트리 이름의 0x7075와 마찬가지로 무시됩니다. -
범용 비트 플래그의 비트 11(언어 인코딩 플래그)이 설정된 경우, 헤더에 있는 주석입니다. 이 주석은 UTF-8입니다.
-
중앙 헤더의 추가 필드 0x554e(Xceed 유니코드)는 주석을 UTF-16LE로 담습니다. 이는 짝이 없는 서로게이트를 보존하는 WTF-8로 변환됩니다.
-
그 밖의 경우에는 주석의 인코딩이 지정되지 않습니다. 압축기는 같은 아카이브의 엔트리 이름에 대해 추측한 인코딩으로 주석을 디코딩할 수 있습니다. 디코딩할 수 없으면 원시 바이트열을
bstr로 사용합니다.
아카이브 주석에는 언어 인코딩 플래그도 추가 필드도 없으므로, 그 인코딩은 아카이브 자체에 의해 지정되는 일이 결코 없습니다. 압축기는 위의 마지막 출처에서와 같이 아카이브 주석을 디코딩할 수 있으며, 그 결과를 archive-comment에 넣습니다.
16.4. ZIP의 Unix 메타데이터
ZIP 아카이브 [APPNOTE]는 엔트리의 Unix 메타데이터를 중앙 헤더의 외부 파일 속성과 추가 필드 0x7875(Info-ZIP UNIX type 3), 0x7855(Info-ZIP UNIX type 2), 0x756e(ASi UNIX), 0x5855(Info-ZIP UNIX type 1), 0x000d(UNIX) [INFOZIP-EXTRAFLD]에 담을 수 있습니다. ZIP 아카이브를 변환하는 압축기는 각 키의 출처를 다음과 같이 선택할 수 있으며, 앞의 출처가 우선하고 같은 항목에 있는 출처는 같은 우선순위를 가집니다.
| 키 | 출처 |
|---|---|
unix-mode
| 외부 파일 속성; 0x756e Mode
|
unix-uid
| 0x7875 UID; 0x7855 UID(로컬 헤더만); 0x756e, 0x5855(로컬 헤더만) 또는 0x000d UID
|
unix-gid
| 0x7875 GID; 0x7855 GID(로컬 헤더만); 0x756e, 0x5855(로컬 헤더만) 또는 0x000d GID
|
각 출처는 다음과 같이 인코딩됩니다.
-
"version made by"의 상위 바이트가 3(UNIX)이면 외부 파일 속성의 상위 16비트는 Unix
st_mode를 담습니다. 그중 권한 비트(0o7777)만unix-mode로 들어가며, 파일 타입 비트(S_IFMT)는 그곳에 저장되지 않습니다. -
버전 1의 0x7875는 사용자 ID와 그룹 ID를 가변 크기의 리틀 엔디언 부호 없는 정수로 저장합니다. [INFOZIP-EXTRAFLD]는 이것이 0x7855보다 우선한다고 규정합니다. 다른 버전은 출처로 사용하지 않습니다.
-
0x7855, 0x756e, 0x5855, 0x000d는 사용자 ID와 그룹 ID를 16비트 부호 없는 정수로 저장합니다. 0x7855의 중앙 헤더 버전은 비어 있으며 로컬 헤더 버전이 존재한다는 것만 나타내고, 0x5855의 중앙 헤더 버전에는 사용자 ID와 그룹 ID가 없습니다.
같은 키에 대한 두 출처가 서로 일치하지 않으면 압축기는 이에 대해 경고할 수 있으며, 그래도 우선하는 출처를 사용합니다.
16.5. 보존하지 않는 추가 필드
다음 추가 필드 [APPNOTE] [INFOZIP-EXTRAFLD]는 zip-extras에 넣지 않습니다. 그 정보가 아카이브 모델과 다른 키에 흡수되거나, ZIP 컨테이너에서만 의미가 있으므로 폐기되기 때문입니다.
| 헤더 ID | 이름 | 처리 |
|---|---|---|
| 0x0001 | Zip64 extended information | 폐기: 크기와 오프셋 |
| 0x0007 | AV Info | 폐기: 서명 |
| 0x000a | NTFS | 흡수: 타임스탬프(§ 16.2 ZIP의 타임스탬프) |
| 0x000d | UNIX (PKWARE) | 흡수: 타임스탬프(§ 16.2 ZIP의 타임스탬프), unix-uid와 unix-gid(§ 16.4 ZIP의 Unix 메타데이터), 링크 노드의 대상, 하드 링크의 link-group, 특수 노드의 장치 번호
|
| 0x0014 | PKCS#7 Store for X.509 Certificates | 폐기: 서명 |
| 0x0015 | X.509 Certificate ID and Signature for individual file | 폐기: 서명 |
| 0x0016 | 중앙 디렉터리용 X.509 Certificate ID | 폐기: 서명 |
| 0x0017 | Strong Encryption 헤더 | 폐기됨: 암호화 |
| 0x0019 | PKCS#7 Encryption Recipient Certificate List | 폐기됨: 암호화 |
| 0x0021 | Policy Decryption Key Record | 폐기됨: 암호화 |
| 0x0022 | Smartcrypt Key Provider Record | 폐기됨: 암호화 |
| 0x0023 | Smartcrypt Policy Key Data Record | 폐기됨: 암호화 |
| 0x10c5 | minizip CMS signature | 폐기: 서명 |
| 0x1986 | Pixar USD 헤더 ID | 폐기됨: 정렬용 패딩 |
| 0x5455 | 확장 타임스탬프 | 흡수됨: 타임스탬프(§ 16.2 ZIP의 타임스탬프) |
| 0x554e | Xceed 유니코드 | 흡수됨: 엔트리 이름(§ 16.1 ZIP의 엔트리 이름) 및 주석(§ 16.3 ZIP의 주석) |
| 0x5855 | Info-ZIP UNIX type 1 | 흡수됨: 타임스탬프(§ 16.2 ZIP의 타임스탬프), unix-uid 및 unix-gid(§ 16.4 ZIP의 Unix 메타데이터)
|
| 0x5a4c | ZipArchive code page | 흡수됨: 엔트리 이름(§ 16.1 ZIP의 엔트리 이름) |
| 0x5a4d | ZipArchive 오프셋 배열 | 폐기됨: 오프셋 |
| 0x6375 | Info-ZIP Unicode Comment | 흡수됨: 주석(§ 16.3 ZIP의 주석) |
| 0x6c78 | libarchive "xl" | 흡수됨: 중앙 헤더 필드의 사본 |
| 0x7075 | Info-ZIP Unicode Path | 흡수됨: 엔트리 이름(§ 16.1 ZIP의 엔트리 이름) |
| 0x7855 | Info-ZIP UNIX type 2 | 흡수됨: unix-uid 및 unix-gid(§ 16.4 ZIP의 Unix 메타데이터)
|
| 0x7875 | Info-ZIP UNIX type 3 | 흡수됨: unix-uid 및 unix-gid(§ 16.4 ZIP의 Unix 메타데이터), 버전 1에 한함
|
| 0x9901 | AE-x(WinZip AES) | 폐기됨: 암호화 |
| 0x9903 | WinZip Reference | 폐기됨: 다른 엔트리에 대한 참조, 변환 시 해소됨 |
| 0xa11e | Data Stream Alignment(Apache Commons Compress) | 폐기됨: 정렬용 패딩 |
| 0xa220 | Microsoft Open Packaging Growth Hint | 폐기됨: 제자리 편집용 패딩 |
| 0xcafe | Java JAR 마커 | 폐기됨: ZIP 파일 자체에 관한 정보 |
| 0xcdcd | minizip 중앙 디렉터리 | 폐기됨: 중앙 디렉터리의 암호화 및 서명 |
| 0xd935 | Android ZIP Alignment | 폐기됨: 정렬용 패딩 |
| 0xe57a | ALZip 코드 페이지 | 흡수됨: 해당 코드 페이지로 디코딩된 경우 엔트리 이름(§ 16.1 ZIP의 엔트리 이름) |
-
0x756e(ASi UNIX)은 0x000d와 같은 방식으로 흡수됩니다. 모드의 권한 비트는
unix-mode로, 사용자 ID와 그룹 ID는unix-uid와unix-gid로, 심볼릭 링크 대상은 링크 노드로 옮겨집니다. 그러나 장치 번호는 비트 배치가 플랫폼마다 다른 원시st_rdev값이므로 특수 노드의 주 번호와 부 번호로 분리할 수 없습니다. 따라서 장치 파일은 원래의 추가 필드가 보존된 빈 파일 노드로 변환됩니다. -
0x4b46(FWKCS MD5)과 0x1a51(minizip 해시)은 압축되지 않은 내용의 다이제스트를 담습니다. 이들은 실제 내용과 일치하는 경우에만 보존됩니다.