본 포스팅은 LVM 2.2.02.187 을 기준으로 작성되었습니다.
디스크 덤프
다음과 같은 환경에서 /dev/sdd
와 /dev/sde
의 덤프를 해보면 어떻게 나올까요?
- PV 2 => VG => LV + Snapshot
# pvs
PV VG Fmt Attr PSize PFree
/dev/sdd thick lvm2 a-- <50.00g 48.98g
/dev/sde thick lvm2 a-- <50.00g <50.00g
# vgs
VG #PV #LV #SN Attr VSize VFree
thick 2 2 1 wz--n- 99.99g 98.98g
# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
thick_snap thick swi-a-s--- 12.00m thick_vol 0.00
thick_vol thick owi-a-s--- 1.00g
- 덤프를 해볼까요?
# dd if=/dev/sdd of=/root/sdd_dump bs=1020k count=1
# dd if=/dev/sde of=/root/sde_dump bs=1020k count=1
- 알아보기 쉽도록 hex 데이터로 바꿔봅시다.
# xxd sdd_dump > sdd_hex
# xxd sde_dump > sde_hex
당연하게도 LVM 라벨이 찍힌 디스크가 보입니다.
# cat sdd_hex | head -n 656
...
0000200: 4c41 4245 4c4f 4e45 0100 0000 0000 0000 LABELONE........
0000210: a982 7799 2000 0000 4c56 4d32 2030 3031 ..w. ...LVM2 001
0000220: 4a6f 6c4f 5177 7833 4466 5033 756a 6e75 JolOQwx3DfP3ujnu
0000230: 776e 6435 4342 4735 6230 6747 5975 3874 wnd5CBG5b0gGYu8t
/dev/sde
도 동일한지 살펴볼까요?
# cat sde_hex | head -n 656
...
0000200: 4c41 4245 4c4f 4e45 0100 0000 0000 0000 LABELONE........
0000210: 1d8e 939d 2000 0000 4c56 4d32 2030 3031 .... ...LVM2 001
0000220: 4647 5a33 4d45 6150 4c68 394e 784f 724f FGZ3MEaPLh9NxOrO
0000230: 6e39 7a38 4172 6459 7071 4f33 6a35 6c52 n9z8ArdYpqO3j5lR
디스크에 쓰여진 데이터 해석
라벨이 찍혀있는 것은 동일하지만 그 뒤 정보는 약간 다른 것을 볼 수 있습니다. 위의 정보는 도대체 뭘까요?
$ cat lib/label/label.h
35 /* On disk - 32 bytes */
36 struct label_header {
37 >---int8_t id[8];>-->---/* LABELONE */
38 >---uint64_t sector_xl;>/* Sector number of this label */
39 >---uint32_t crc_xl;>---/* From next field to end of sector */
40 >---uint32_t offset_xl;>/* Offset from start of struct to contents */
41 >---int8_t type[8];>>---/* LVM2 001 */
42 } __attribute__ ((packed));
LVM 소스에서 확인해보니 라벨
, 섹터정보
, crc 정보
, offset 정보
, LVM 타입
등이 보이네요.
아 그러면은 LVM 에서 사용하는 실제 섹터가 어디에 위치에 있는지 확인도 하고 디스크가 LVM 에서 쓰이는지 확인하려고 라벨을 달아둔 것 같네요~
그 다음 내용은 뭘까요?
# cat sde_hex | head -n 656
...
0000220: 4647 5a33 4d45 6150 4c68 394e 784f 724f FGZ3MEaPLh9NxOrO
0000230: 6e39 7a38 4172 6459 7071 4f33 6a35 6c52 n9z8ArdYpqO3j5lR
0000240: 0000 0080 0c00 0000 0000 1000 0000 0000 ................
0000250: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000260: 0000 0000 0000 0000 0010 0000 0000 0000 ................
0000270: 00f0 0f00 0000 0000 0000 0000 0000 0000 ................
0000280: 0000 0000 0000 0000 0200 0000 0100 0000 ................
0000290: 0000 0000 0000 0000 0000 0000 0000 0000 ................
$ cat lib/format_text/layout.h
40 /* Fields with the suffix _xl should be xlate'd wherever they appear */
41 /* On disk */
42 struct pv_header {
43 >---int8_t pv_uuid[ID_LEN];
44
45 >---/* This size can be overridden if PV belongs to a VG */
46 >---uint64_t device_size_xl;>---/* Bytes */
47
48 >---/* NULL-terminated list of data areas followed by */
49 >---/* NULL-terminated list of metadata area headers */
50 >---struct disk_locn disk_areas_xl[0];>-/* Two lists */
51 } __attribute__ ((packed));
52
53 /*
54 * Ignore this raw location. This allows us to
pvcreate 시에 pvuuid 의 정보에 대해서 저장을 해뒀나 보네요. pv 의 uuid
와 디바이스의 정보를 포함하고 있습니다.
disk_areas_xl
에는 각각 데이터 영역 정보와 메타데이터 영역 정보를 포함하고 있습니다. 영역 정보는 리스트로 들어갈 수 있는데, 리스트가 끝났다면 zero
값으로 offset
과 size
를 채워넣습니다.
$ cat lib/format_text/format-text.h
67 /* On disk */
68 struct disk_locn {
69 >---uint64_t offset;>---/* Offset in bytes to start sector */
70 >---uint64_t size;>->---/* Bytes */
71 } __attribute__ ((packed));
아래에서 보면 0000 1000 0000 0000
값이 데이터 영역의 offset, 0000 0000 0000 0000
이 데이터 영역의 size 임을 확인할 수 있습니다.
offset
,size
등은bswap
을 사용하여 저장하고 읽습니다. 위의0000 1000 0000 0000
는 그러므로x100000
값으로 읽을 수 있습니다.
0000240: 0000 0080 0c00 0000 0000 1000 0000 0000 ................
0000250: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000260: 0000 0000 0000 0000 0010 0000 0000 0000 ................
0000270: 00f0 0f00 0000 0000 0000 0000 0000 0000 ................
0000280: 0000 0000 0000 0000 0200 0000 0100 0000 ................
데이터 영역의 위치로 가면 아직 아무것도 작성을 하지 않아서 그런지 아무것도 없습니다.
0100000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0100010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0100020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0100030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0100040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0100050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0100060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0100070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0100080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
하지만 메타데이터 영역의 위치(x10 00) 으로 가면 메타데이터의 헤더가 있는 것을 확인할 수 있죠.
0001000: 2af4 634e 204c 564d 3220 785b 3541 2572 *.cN LVM2 x[5A%r
0001010: 304e 2a3e 0100 0000 0010 0000 0000 0000 0N*>............
0001020: 00f0 0f00 0000 0000 00a0 0100 0000 0000 ................
0001030: 0007 0000 0000 0000 a181 917c 0000 0000 ...........|....
0001040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0001050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0001060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0001070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0001080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
메타데이터 헤더는 어떻게 구성되어 있을까요?
$ cat lib/format_text/layout.h
71 /* On disk */
72 /* Structure size limited to one sector */
73 struct mda_header {
74 >---uint32_t checksum_xl;>--/* Checksum of rest of mda_header */
75 >---int8_t magic[16];>--/* To aid scans for metadata */
76 >---uint32_t version;
77 >---uint64_t start;>>---/* Absolute start byte of mda_header */
78 >---uint64_t size;>->---/* Size of metadata area */
79
80 >---struct raw_locn raw_locns[0];>--/* NULL-terminated list */
81 } __attribute__ ((packed));
60 /* On disk */
61 struct raw_locn {
62 >---uint64_t offset;>---/* Offset in bytes to start sector */
63 >---uint64_t size;>->---/* Bytes */
64 >---uint32_t checksum;
65 >---uint32_t flags;
66 } __attribute__ ((packed));
메타데이터 헤더는 헤더 정보의 유효성을 검증하고 메타데이터의 위치를 가리키고 있습니다.
실제로 메타데이터는 mda_header->start
+ raw_locns->offset
에서 확인이 가능합니다.
한번 가 볼까요?
- 이제 주소를 계산하는 것도 어렵지 않습니다!
x1000 + x1a000 = x1b000
001b000: 7468 6963 6b20 7b0a 6964 203d 2022 3266 thick {.id = "2f
001b010: 7559 4431 2d48 5856 792d 315a 3275 2d76 uYD1-HXVy-1Z2u-v
001b020: 797a 332d 7765 4530 2d6c 6553 4a2d 716e yz3-weE0-leSJ-qn
001b030: 6d79 784f 220a 7365 716e 6f20 3d20 3333 myxO".seqno = 33
001b040: 0a66 6f72 6d61 7420 3d20 226c 766d 3222 .format = "lvm2"
001b050: 0a73 7461 7475 7320 3d20 5b22 5245 5349 .status = ["RESI
001b060: 5a45 4142 4c45 222c 2022 5245 4144 222c ZEABLE", "READ",
001b070: 2022 5752 4954 4522 5d0a 666c 6167 7320 "WRITE"].flags.
001b080: 3d20 5b5d 0a65 7874 656e 745f 7369 7a65 = [].extent_size
참고
'DevOps > 지식창고' 카테고리의 다른 글
Devel (0) | 2024.04.01 |
---|---|
Samba VFS (0) | 2024.03.30 |
LVM Thin (0) | 2024.02.11 |
NFS (0) | 2023.12.16 |
Paxos 기능 개발기 - 개념 정리(2) (0) | 2023.12.15 |
Paxos 기능 개발기 - 개념정리(1) (1) | 2023.12.11 |
VDI(Virtual Desktop Infrastructur (1) | 2023.12.07 |
SAN (0) | 2023.12.02 |