refactor: 문서 개선 중

This commit is contained in:
donghyeon-ka
2026-09-21 14:30:55 +09:00
parent c93cdea150
commit 805a18f486
1497 changed files with 525837 additions and 59152 deletions
@@ -0,0 +1,26 @@
# qcow2 L1·L2 매핑과 backing fallback
## Alternative text
게스트 오프셋이 L1과 L2 표를 지나 현재 qcow2 데이터 클러스터로 가거나, L2 항목이 0일 때 backing file 또는 zero-fill로 갈라지는 qcow2 매핑도.
## Long description
게스트가 읽으려는 위치는 L1 표와 L2 표를 통해 파일 안 데이터 클러스터 오프셋으로 변환된다. 매핑된 값이 있으면 현재 qcow2의 데이터 클러스터를 읽는다. 값이 0이고 backing file이 있으면 base.qcow2의 같은 가상 위치를 읽고, backing file이 없으면 0으로 채운 64KB를 반환한다. backing file 경로는 qcow2 헤더의 문자열이므로 오버레이만 옮기면 부팅할 수 없다는 제한도 그림 끝에 반영한다.
## Elements and evidence
- **Guest offset** (data): 게스트가 읽으려는 가상 디스크 위치. Evidence: L14317L14325.
- **L1 table** (component): 필요한 L2 표가 있는 클러스터 위치를 찾는다. Evidence: L14312L14337.
- **L2 entry** (component): 현재 qcow2 데이터 클러스터 오프셋을 가지거나 0일 수 있다. Evidence: L14320L14347.
- **현재 qcow2 data cluster** (store): L2 값이 있을 때 같은 qcow2 파일 안에서 읽는 데이터 클러스터. Evidence: L14267L14272, L14343L14345.
- **base.qcow2** (store): L2 항목이 0이고 backing file이 있을 때 같은 가상 위치를 읽는 바닥 파일. Evidence: L14343L14354.
- **zero-filled 64KB** (result): L2 항목이 0이고 backing file도 없을 때 만들어 반환하는 0 데이터. Evidence: L14343L14347.
## Relationships
- **L1 table → L2 entry:** L2 cluster. Evidence: L14320L14337.
- **L2 entry → base.qcow2:** 0 + backing. Evidence: L14343L14350.
- **L2 entry → 현재 qcow2 data cluster:** mapped. Evidence: L14343L14345.
- **L2 entry → zero-filled 64KB:** 0 + no backing. Evidence: L14343L14347.
- **Guest offset → L1 table:** 상위 비트. Evidence: L14317L14324.
@@ -0,0 +1,26 @@
# qcow2 L1·L2 매핑과 backing fallback
# Question: 게스트 오프셋은 qcow2 파일의 데이터 클러스터로 어떻게 매핑되고, L2 항목이 0일 때 왜 backing file이나 zero-fill로 갈리는가
direction: right
n0: "Guest offset" {
shape: rectangle
}
n1: "L1 table" {
shape: rectangle
}
n2: "L2 entry" {
shape: rectangle
}
n3: "현재 qcow2 data cluster" {
shape: rectangle
}
n4: "base.qcow2" {
shape: rectangle
}
n5: "zero-filled 64KB" {
shape: rectangle
}
n0 -> n1: "상위 비트"
n1 -> n2: "L2 cluster"
n2 -> n3: "mapped"
n2 -> n4: "0 + backing"
n2 -> n5: "0 + no backing"
@@ -0,0 +1,16 @@
digraph techviz {
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
n0 [label="Guest offset", shape=box, style="rounded,filled"];
n1 [label="L1 table", shape=box, style="rounded,filled"];
n2 [label="L2 entry", shape=box, style="rounded,filled"];
n3 [label="현재 qcow2 data cluster", shape=box, style="rounded,filled"];
n4 [label="base.qcow2", shape=box, style="rounded,filled"];
n5 [label="zero-filled 64KB", shape=box, style="rounded,filled"];
n0 -> n1 [label="상위 비트", style=solid];
n1 -> n2 [label="L2 cluster", style=solid];
n2 -> n3 [label="mapped", style=solid];
n2 -> n4 [label="0 + backing", style=solid];
n2 -> n5 [label="0 + no backing", style=solid];
}
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
<diagram id="qcow2-mapping-clusters" name="qcow2 L1·L2 매핑과 backing fallback">
<mxGraphModel dx="1257" dy="448" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1257" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="n_guest-offset" value="Guest offset&lt;br/&gt;virtual disk position" tooltip="게스트가 읽으려는 가상 디스크 위치. | Evidence: L14317-L14325" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="70.0" y="196.0" width="181.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_l1" value="L1 table&lt;br/&gt;상위 비트" tooltip="필요한 L2 표가 있는 클러스터 위치를 찾는다. | Evidence: L14312-L14337" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="411.0" y="196.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_l2" value="L2 entry&lt;br/&gt;중간 13비트" tooltip="현재 qcow2 데이터 클러스터 오프셋을 가지거나 0일 수 있다. | Evidence: L14320-L14347" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="721.0" y="196.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_backing" value="base.qcow2&lt;br/&gt;backing file" tooltip="L2 항목이 0이고 backing file이 있을 때 같은 가상 위치를 읽는 바닥 파일. | Evidence: L14343-L14354" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;" vertex="1" parent="1">
<mxGeometry x="1046.5" y="60.0" width="150.0" height="71.0" as="geometry"/>
</mxCell>
<mxCell id="n_local-cluster" value="현재 qcow2 data cluster" tooltip="L2 값이 있을 때 같은 qcow2 파일 안에서 읽는 데이터 클러스터. | Evidence: L14267-L14272, L14343-L14345" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;" vertex="1" parent="1">
<mxGeometry x="1031.0" y="203.0" width="181.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_zero-fill" value="zero-filled 64KB" tooltip="L2 항목이 0이고 backing file도 없을 때 만들어 반환하는 0 데이터. | Evidence: L14343-L14347" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="1046.5" y="339.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="e_l1-l2" value="L2 cluster" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_l1" target="n_l2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="641.0" y="203.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_l2-backing" value="0 + backing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_l2" target="n_backing">
<mxGeometry relative="1" as="geometry">
<mxPoint x="982.8" y="154.5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_l2-local" value="mapped" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_l2" target="n_local-cluster">
<mxGeometry relative="1" as="geometry">
<mxPoint x="975.0" y="233.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_l2-zero" value="0 + no backing" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_l2" target="n_zero-fill">
<mxGeometry relative="1" as="geometry">
<mxPoint x="982.8" y="310.2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_offset-l1" value="상위 비트" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-offset" target="n_l1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="331.0" y="203.5" as="offset"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
@@ -0,0 +1,922 @@
{
"type": "excalidraw",
"version": 2,
"source": "techviz-harness",
"elements": [
{
"id": "edge-l1-l2",
"type": "arrow",
"x": 561.0,
"y": 231.5,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 586743950,
"version": 1,
"versionNonce": 1823449811,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
0.0
],
[
80.0,
0.0
],
[
80.0,
0.0
],
[
160.0,
0.0
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-l1",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-l2",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-l1-l2",
"type": "text",
"x": 596.0,
"y": 191.5,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1344248471,
"version": 1,
"versionNonce": 1292323506,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "L2 cluster",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "L2 cluster",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-l2-backing",
"type": "arrow",
"x": 871.0,
"y": 95.5,
"width": 175.5,
"height": 118.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 1567107232,
"version": 1,
"versionNonce": 1394231586,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
118.0
],
[
87.75,
118.0
],
[
87.75,
0.0
],
[
175.5,
0.0
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-l2",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-backing",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-l2-backing",
"type": "text",
"x": 937.75,
"y": 142.5,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 751142759,
"version": 1,
"versionNonce": 1636292120,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "0 + backing",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "0 + backing",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-l2-local",
"type": "arrow",
"x": 871.0,
"y": 231.5,
"width": 160.0,
"height": 3.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 205827981,
"version": 1,
"versionNonce": 1468667393,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
0.0
],
[
80.0,
0.0
],
[
80.0,
3.5
],
[
160.0,
3.5
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-l2",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-local-cluster",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-l2-local",
"type": "text",
"x": 930.0,
"y": 221.25,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1218557231,
"version": 1,
"versionNonce": 919123626,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "mapped",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "mapped",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-l2-zero",
"type": "arrow",
"x": 871.0,
"y": 249.5,
"width": 175.5,
"height": 121.5,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 1572116611,
"version": 1,
"versionNonce": 650463629,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
0.0
],
[
87.75,
0.0
],
[
87.75,
121.5
],
[
175.5,
121.5
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-l2",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-zero-fill",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-l2-zero",
"type": "text",
"x": 926.75,
"y": 298.25,
"width": 112,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1455255978,
"version": 1,
"versionNonce": 35062350,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "0 + no backing",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "0 + no backing",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-offset-l1",
"type": "arrow",
"x": 251.0,
"y": 231.5,
"width": 160.0,
"height": 0.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": null,
"seed": 1376738291,
"version": 1,
"versionNonce": 1345642548,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"points": [
[
0.0,
0.0
],
[
80.0,
0.0
],
[
80.0,
0.0
],
[
160.0,
0.0
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "node-guest-offset",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-l1",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-offset-l1",
"type": "text",
"x": 286.0,
"y": 191.5,
"width": 90,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1804422112,
"version": 1,
"versionNonce": 797123369,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "상위 비트",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "상위 비트",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-guest-offset",
"type": "rectangle",
"x": 70.0,
"y": 196.0,
"width": 181.0,
"height": 71.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1190817156,
"version": 1,
"versionNonce": 139318315,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-guest-offset",
"type": "text",
"x": 80.0,
"y": 206.0,
"width": 161.0,
"height": 51.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 83110366,
"version": 1,
"versionNonce": 49655676,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "Guest offset\nvirtual disk position",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Guest offset\nvirtual disk position",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-l1",
"type": "rectangle",
"x": 411.0,
"y": 196.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1774178777,
"version": 1,
"versionNonce": 229505492,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-l1",
"type": "text",
"x": 421.0,
"y": 206.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 15495054,
"version": 1,
"versionNonce": 244208457,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "L1 table\n상위 비트",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "L1 table\n상위 비트",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-l2",
"type": "rectangle",
"x": 721.0,
"y": 196.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 207729084,
"version": 1,
"versionNonce": 618174549,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-l2",
"type": "text",
"x": 731.0,
"y": 206.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 248933745,
"version": 1,
"versionNonce": 1248263086,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "L2 entry\n중간 13비트",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "L2 entry\n중간 13비트",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-backing",
"type": "rectangle",
"x": 1046.5,
"y": 60.0,
"width": 150.0,
"height": 71.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 241530005,
"version": 1,
"versionNonce": 110043311,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-backing",
"type": "text",
"x": 1056.5,
"y": 70.0,
"width": 130.0,
"height": 51.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1088329617,
"version": 1,
"versionNonce": 1471763197,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "base.qcow2\nbacking file",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "base.qcow2\nbacking file",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-local-cluster",
"type": "rectangle",
"x": 1031.0,
"y": 203.0,
"width": 181.0,
"height": 64.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1087803653,
"version": 1,
"versionNonce": 174713902,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-local-cluster",
"type": "text",
"x": 1041.0,
"y": 213.0,
"width": 161.0,
"height": 44.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1290752056,
"version": 1,
"versionNonce": 635359524,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "현재 qcow2 data cluster",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "현재 qcow2 data cluster",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-zero-fill",
"type": "rectangle",
"x": 1046.5,
"y": 339.0,
"width": 150.0,
"height": 64.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 1358300415,
"version": 1,
"versionNonce": 304765438,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-zero-fill",
"type": "text",
"x": 1056.5,
"y": 349.0,
"width": 130.0,
"height": 44.0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": null,
"roundness": {
"type": 3
},
"seed": 720572336,
"version": 1,
"versionNonce": 603273159,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "zero-filled 64KB",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "zero-filled 64KB",
"autoResize": true,
"lineHeight": 1.25
}
],
"appState": {
"gridSize": 10,
"viewBackgroundColor": "#ffffff",
"currentItemFontFamily": 5
},
"files": {}
}
@@ -0,0 +1,42 @@
{
"harness_version": "0.2.0",
"spec_id": "qcow2-mapping-clusters",
"spec_version": "1.1",
"spec_sha256": "7f8a8cdf4d78168e56ca829ead8d8e574479d11d431891d3d9e5fce5b40635a4",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210",
"anchor": {
"kind": "heading",
"value": "231. qcow2 파일 내부는 어떻게 생겼나 — 매핑표가 전부다",
"line": 14221
}
},
"outputs": [
"qcow2-mapping-clusters.svg",
"qcow2-mapping-clusters.mmd",
"qcow2-mapping-clusters.d2",
"qcow2-mapping-clusters.dot",
"qcow2-mapping-clusters.drawio",
"qcow2-mapping-clusters.excalidraw",
"qcow2-mapping-clusters.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true,
"source_spec_file_sha256": "12590f917b1fa302a293b29584e40de224f9b280f479110128cde4878b89cd65",
"output_sha256": {
"qcow2-mapping-clusters.svg": "9668c9c2e9a6e67d290833f4c055607744c6b8448c89fdb5c2e374a1832e3324",
"qcow2-mapping-clusters.mmd": "614782850ad8082f2d2b8b6eadd5969d3a71e6fb77fb08173b87420a1b1b16a3",
"qcow2-mapping-clusters.d2": "5508d5d8d5496f41b537ae94d98ceb4f42d685ae8d0b0ae895827ce8b52949a1",
"qcow2-mapping-clusters.dot": "e5423cf82ef26c98d391e7f5553959332ebe4808c28ac4e74a93450c04c4a656",
"qcow2-mapping-clusters.drawio": "bfcea347243b06067ec55e63f582e2d07213a1c76ddbbc7df907a5c5feb67506",
"qcow2-mapping-clusters.excalidraw": "8fa0e7c79497e1e13223ebc2601c57872c5dcb58cb2bf7e2bd8008b5ef3d1c98",
"qcow2-mapping-clusters.alt.md": "f72433e1f4f80441e41bd668dd67818e5837a14e2a3b0a1b703fa2e14c3bfda6"
}
}
@@ -0,0 +1,14 @@
%% qcow2 L1·L2 매핑과 backing fallback
%% question: 게스트 오프셋은 qcow2 파일의 데이터 클러스터로 어떻게 매핑되고, L2 항목이 0일 때 왜 backing file이나 zero-fill로 갈리는가
flowchart LR
n0["Guest offset"]
n1["L1 table"]
n2["L2 entry"]
n3["현재 qcow2 data cluster"]
n4["base.qcow2"]
n5["zero-filled 64KB"]
n0 -->|"상위 비트"| n1
n1 -->|"L2 cluster"| n2
n2 -->|"mapped"| n3
n2 -->|"0 + backing"| n4
n2 -->|"0 + no backing"| n5
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1257" height="448" viewBox="0 0 1257 448" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">qcow2 L1·L2 매핑과 backing fallback</title>
<desc id="diagram-description">게스트가 읽으려는 위치는 L1 표와 L2 표를 통해 파일 안 데이터 클러스터 오프셋으로 변환된다. 매핑된 값이 있으면 현재 qcow2의 데이터 클러스터를 읽는다. 값이 0이고 backing file이 있으면 base.qcow2의 같은 가상 위치를 읽고, backing file이 없으면 0으로 채운 64KB를 반환한다. backing file 경로는 qcow2 헤더의 문자열이므로 오버레이만 옮기면 부팅할 수 없다는 제한도 그림 끝에 반영한다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;qcow2-mapping-clusters&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8c4ecc64c8cea9a4450ed7131fdd9cb2048dc092b66cd969f6346ed77887c210&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;231. qcow2 파일 내부는 어떻게 생겼나 — 매핑표가 전부다&quot;,&quot;line&quot;:14221}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1257" height="448" />
<polyline class="edge kind-data style-solid emphasis-normal" points="561.0,231.5 641.0,231.5 641.0,231.5 721.0,231.5" data-evidence="14320-14337" />
<rect class="edge-label-bg" x="598.5" y="189.5" width="85.0" height="22" rx="3" />
<text class="edge-label" x="641.0" y="204.5">L2 cluster</text>
<polyline class="edge kind-data style-dashed emphasis-normal" points="871.0,213.5 958.8,213.5 958.8,95.5 1046.5,95.5" data-evidence="14343-14350" />
<rect class="edge-label-bg" x="936.9" y="140.5" width="91.7" height="22" rx="3" />
<text class="edge-label" x="982.8" y="155.5">0 + backing</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="871.0,231.5 951.0,231.5 951.0,235.0 1031.0,235.0" data-evidence="14343-14345" />
<rect class="edge-label-bg" x="945.9" y="219.2" width="58.2" height="22" rx="3" />
<text class="edge-label" x="975.0" y="234.2">mapped</text>
<polyline class="edge kind-result style-dashed emphasis-normal" points="871.0,249.5 958.8,249.5 958.8,371.0 1046.5,371.0" data-evidence="14343-14347" />
<rect class="edge-label-bg" x="926.9" y="296.2" width="111.8" height="22" rx="3" />
<text class="edge-label" x="982.8" y="311.2">0 + no backing</text>
<polyline class="edge kind-data style-solid emphasis-normal" points="251.0,231.5 331.0,231.5 331.0,231.5 411.0,231.5" data-evidence="14317-14324" />
<rect class="edge-label-bg" x="305.2" y="189.5" width="51.5" height="22" rx="3" />
<text class="edge-label" x="331.0" y="204.5">상위 비트</text>
<g id="node-guest-offset">
<rect class="node-shape kind-data emphasis-normal role-source" data-evidence="14317-14325" x="70.0" y="196.0" width="181.0" height="71.0" rx="7" />
<text class="node-label" x="160.5" y="223.0">Guest offset</text>
<line class="node-detail-divider" x1="84.0" y1="244.0" x2="237.0" y2="244.0" />
<text class="node-detail" x="86.0" y="261.0">virtual disk position</text>
</g>
<g id="node-l1">
<rect class="node-shape kind-component emphasis-normal role-service" data-evidence="14312-14337" x="411.0" y="196.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="486.0" y="223.0">L1 table</text>
<line class="node-detail-divider" x1="425.0" y1="244.0" x2="547.0" y2="244.0" />
<text class="node-detail" x="427.0" y="261.0">상위 비트</text>
</g>
<g id="node-l2">
<rect class="node-shape kind-component emphasis-primary role-service" data-evidence="14320-14347" x="721.0" y="196.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="796.0" y="223.0">L2 entry</text>
<line class="node-detail-divider" x1="735.0" y1="244.0" x2="857.0" y2="244.0" />
<text class="node-detail" x="737.0" y="261.0">중간 13비트</text>
</g>
<g id="node-backing">
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="14343-14354" x="1046.5" y="71.8" width="150.0" height="47.3" /><ellipse class="node-shape kind-store emphasis-normal role-store" cx="1121.5" cy="71.8" rx="75.0" ry="11.8" /><path class="storage-bottom" d="M 1046.5 119.2 A 75.0 11.8 0 0 0 1196.5 119.2" />
<text class="node-label" x="1121.5" y="87.0">base.qcow2</text>
<line class="node-detail-divider" x1="1060.5" y1="108.0" x2="1182.5" y2="108.0" />
<text class="node-detail" x="1062.5" y="125.0">backing file</text>
</g>
<g id="node-local-cluster">
<rect class="node-shape kind-store emphasis-normal role-store" data-evidence="14267-14272,14343-14345" x="1031.0" y="213.7" width="181.0" height="42.7" /><ellipse class="node-shape kind-store emphasis-normal role-store" cx="1121.5" cy="213.7" rx="90.5" ry="10.7" /><path class="storage-bottom" d="M 1031.0 256.3 A 90.5 10.7 0 0 0 1212.0 256.3" />
<text class="node-label" x="1121.5" y="233.0">현재 qcow2 data cluster</text>
</g>
<g id="node-zero-fill">
<rect class="node-shape kind-result emphasis-normal role-sink" data-evidence="14343-14347" x="1046.5" y="339.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="1121.5" y="369.0">zero-filled 64KB</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.4 KiB