Mounten eines vmdk-Laufwerks im Linux Host – III – qemu-nbd, loop-devices, kpartx

In dieser Artikelserie beschäftigen wir uns damit, wie man von einer Linux-Umgebung aus direkt auf Partitionen von vmdk-Disk-Images zugreifen kann. In den letzten beiden Artikeln

Mounten eines vmdk Laufwerks im Linux Host – I – vmware-mount
Mounten eines vmdk-Laufwerks im Linux Host – II – Einschub, Spezifikation, Begriffe

hatten wir zunächst das Kommando “vmware-mount” betrachtet. “vmware-mount” kann Partitionen in Snapshots eines “sparse and growing vmdk-Images” direkt auf Zielverzeichnisse eines Linux-Baums mounten. Wir hatten dabei gesehen, dass zwischenzeitlich ein sog. “flat”-File angelegt wird. Die Rechtesetzung beim Mounten hatte uns noch nicht besonders gefallen. Da vmdk-Format unterschiedliche und komplexe Varianten von bedarfsgerecht wachsenden Images und Snapshots erlaubt, hatten wir im zweiten Artikel ein paar Seitenblicke auf die Spezifikation geworfen, um die Vielzahl von vmdk-Dateien zu einem Disk-Image und die zugehörige Nomenklatur besser zu verstehen. Nun wollen wir ein erstes natives Linux-Tools betrachten.

Da “vmdk” mit Virtualisierung zu tun hat, ist es kein Wunder, dass die beste Unterstützung für dieses Format unter Linux aus dem QEMU-Bereich – und damit von Red Hat und aufgekauften Firmen – kommt. Eines der Tools, auf die dabei inzwischen Verlass ist, gibt es schon sehr lange (seit etwa 2010): qemu-nbd.

nbd steht dabei für Network Block Device; Ziel des nbd-Toolsets war es, virtuelle Speichermedien auch über Server – also über Netz – für qemu-basierte virtuelle Maschinen auf Client-Systemen (z.B. Linux-Workstations) bereitzustellen. Schön beschrieben sind die Grundlagen aus den Anfangszeiten etwa hier :
Qemu-Buch zu Network Block Devices
https://de.wikipedia.org/wiki/Network_Block_Device

nbd kann aber natürlich auch lokal – also auf der eigenen Linux-Workstation – zur Erstellung eines Block-Devices auf Basis eines vmdk-Disk-Images eingesetzt werden. Das Kernelmodul “nbd” und das zugehörige CLI-Kommando “qemu-nbd” erweisen sich dabei als fähig, auch Snapshots des neuesten vmdk-Formats in der Version 6 richtig zu verarbeiten. Für die weitere Verwertung unter Linux werden – bei richtiger Parametrierung – ein oder mehrere ein Block-Devices erzeugt, die wir z.T. direkt mounten können.

Sicherheit und das direkte Mounten von virtuellen Disks/Filesystemen auf Linux-Hosts

Vorab ein paar mahnende Worte: vmdk, qcow2, etc. sind für virtuelle Maschinen gedacht. Gerade bei virtualisierten Windows-Maschinen, aber auch sonst, sollte man vorsichtig sein, wenn man nicht genau weiß, in welchem Zustand sich die Filesysteme des Images befinden. Ein manipuliertes Filesystem und/oder mit Malware behafteter Inhalt kann nach einem Mounten auch auf einem Linux-Host erheblichen Schaden anrichten. Leute, die etwas anderes glauben, erliegen einer Illusion.

Ich plädiere deshalb dafür, Experimente oder auch forensische Aktivitäten mit Partitionen aus vmdk-Images immer in einem virtuellen KVM-Linux-Gastsystem eines KVM-Hostes auszuführen. Das Gastsystem kann man hinreichend gut vom eigentlichen Virtualisierungs-Host isolieren. Die Performance ist auf aktuellen und SSD-basierten Systemen hinreichend gut, um auch mit großen vmdk-Images bequem hantieren zu können.

Wenn ich nachfolgend vom “Host” spreche, meine ich also immer das Linux-System, auf dem man mit dem vmdk-Image “forensisch” operiert – und nicht die virtuelle
Maschine, die das Image normalerweise direkt nutzt und auch nicht den Virtualisierungshost. Ich meine vielmehr einen speziellen KVM-Linux-Gast, von dem aus man auf das Image zugreift. Die zu untersuchenden vmdk-Dateien kann man auf einem solchen Gast z.B. über SSHFS bereitstellen – oder sie bei hinreichendem Platz einfach per scp in das Gast-Filesystem hineinkopieren.

Man lese zu den Gefahren etwa:
A reminder why you should never mount guest disk images on the host OS von D.P. Berrange

Zudem gilt immer:
Mehrfache Mounts mit ggf. konkurrierenden schreibenden Zugriffen sind unbedingt zu vermeiden! Operiert man mit dem Image entgegen meinem Ratschlag direkt auf dem Virtualisierungshost, so muss das virtualisierte Gastsystem, das das Image normalerweise nutzt, abgeschaltet sein. Oder: Man mountet zur Sicherheit in einem “read only”-Modus.

Notwendige Pakete für qemu-nbd

Wer sich unter Linux – in meinem Fall Opensuse Leap – mit qemu und KVM auseinandersetzt, hat die notwendigen Pakete mit ziemlicher Sicherheit bereits installiert. Erforderlich ist das Paket “qemu-tools” (unter Debian-Derivaten “qemu-utils”). Abhängigkeiten werden durch YaST (oder apt-get) aufgelöst. Unter Opensuse Leap ist das Paket bereits im Standard-Update-Repository enthalten; alternativ kann man auf das Virtualization Repository zurückgreifen.

Unterstützte vmdk-Formate

qemu-nbd greift intern auf die Fähigkeiten von “qemu-img” zurück. Die Seite en.wikibooks.org-wiki-QEMU-Images informiert darüber, welche vmdk-Formate (neben vielen anderen Formaten) qemu-img in einer aktuellen Version (≥ 2.9) unterstützt:

vmdk:
VMware 3 & 4, or 6 image format, for exchanging images with that product

Das Interessante ist, dass qemu-nbd die vorgegebenen vmdk-Dateien zu Snapshots und zur vmdk-Base-Disk nach außen – also in Richtung Linux-User – zu einem Block-Device zusammenführt. qemu-nbd legt also einen Block-Layer über die komplexe vmdk-Adressierungsstruktur. Die Hauptarbeit leistet dabei ein Kernelmodul.

Dreisatz zur Anwendung von qemu-nbd

Drei Schritte sind notwendig, um zu dem gewünschten Block-Devices und darin enthaltenen Filesysteme zu mounten.

Schritt 1 – Kernel-Modul laden:
Zunächst muss das “nbd”-Kernel-Modul geladen werden. Die Seite “kernel.org-Documentation-zu-nb erläutert die möglichen Parameter. Dieselbe Info liefert natürlich auch “modinfo”:

mytux:~ # modinfo nbd
filename:       /lib/modules/4.4.120-45-default/kernel/drivers/block/nbd.ko
license:        GPL
description:    Network Block Device
srcversion:     6F062B770FED9DC58072736
depends:        
retpoline:      Y
intree:         Y
vermagic:       4.4.120-45-default SMP mod_unload modversions 
signer:         openSUSE Secure Boot Signkey
sig_key:        03:32:FA:9C:BF:0D:88:BF:21:92:4B:0D:E8:2A:09:A5:4D:5D:EF:C8
sig_hashalgo:   sha256
parm:           nbds_max:number of network block devices to initialize (default: 16) (int)
parm:           max_part:number of partitions per device (default: 0) (int)

In meinem Testfall erwarte ich maximal 4 (NTFS/FAT-) Partitionen pro vmdk-Device, also:

mytux:/etc # modprobe nbd max-part=4
mytux:/etc # 

Schritt 2 – nbd-Device wählen und mit dem Disk-Image verknüpfen:
Defaultmäßig hält Linux 15 potentielle nbd-Devices unter dem Verzeichnis “/dev” vor. Nun muss ein solches “nbd”-Block-Device natürlich noch mit einem Disk-Image
verbunden werden. Wurde das Device “/dev/nbdx” – alos z.B. “/dev/nbd0” – noch nicht anderweitig benutzt, können wir es mit einem Disk-Image mittels der “-c” (= –connect) Option des Kommandos “<strong>qemu-nbd</strong>” zusammenführen.

Vorher müssen wir ein geeignetes Image wählen. Leser meines letzten Artikels wissen, dass auf meinem (selbst virtualisierten) Linux-System ein Testverzeichnis mit Dateien eines Win7-Gastes einer VMware-Umgebung existiert. Das Verzeichnis beinhaltet u.a. den zweiten Snapshot eines growable, sparse vmdk-Disk-Images zu einer Disk “Win7_x64_ssdx”. Um es noch komplizierter zu machen, befinden sich die ursprüngliche Deskriptor-Datei und die zugehörigen Extent-Dateien (inkl. der ursprünglichen Basis-Datei) in einem anderen Verzeichnis “/vmw/Win7”.

Das Verzeichnis “/vmw/Win7Test/” beinhaltet dagegen die Delta-Dateien (Deskriptor und Extents):

mytux:/vmwssd_w7prod/Win7_x64 # la | grep ssdx
-rw------- 1 myself  users    1507328 Mar 28 20:26 Win7_x64_ssdx-000001-s001.vmdk
-rw------- 1 myself  users      65536 Mar 28 19:54 Win7_x64_ssdx-000001-s002.vmdk
-rw------- 1 myself  users        370 Mar 28 20:24 Win7_x64_ssdx-000001.vmdk
-rw------- 1 myself  users     851968 Mar 29 10:38 Win7_x64_ssdx-000002-s001.vmdk
-rw------- 1 myself  users      65536 Mar 28 20:26 Win7_x64_ssdx-000002-s002.vmdk
-rw------- 1 myself  users      10240 Mar 29 10:37 Win7_x64_ssdx-000002.vmdk

Die Verlinkung zu den Ursprungsdateien unter “/vmw/Win7”

mytux:/vmwssd_w7prod/Win7_x64 # la /vmw/Win7/ | grep ssdx
-rw-------  1 myself  users 2344157184 Mar 28 19:53 Win7_x64_ssdx-s001.vmdk
-rw-------  1 myself  users     131072 Mar 27 19:37 Win7_x64_ssdx-s002.vmdk
-rw-------  1 myself  users        511 Mar 28 19:51 Win7_x64_ssdx.vmdk

ist, wie wir aus dem vorhergehenden Artikel wissen, natürlich über Verweise in den Deskriptor-Dateien Win7_x64_ssdx-000002.vmdk und Win7_x64_ssdx-000001.vmdk der Snapshots festgelegt.

Kommt qemu-nbd mit dieser komplexen Struktur klar? Ja – und wir müssen dabei nur die richtige Deskriptor-Datei angeben …

mytux:/vmw/Win7Test # qemu-nbd -c /dev/nbd0 Win7_x64_ssdx-000002.vmdk 
mytux:/vmw/Win7Test # la /dev | grep nbd0
brw-rw----   1 root disk       43,   0 Mar 30 14:51 nbd0
brw-rw----   1 root disk       43,   1 Mar 30 14:51 nbd0p1
brw-rw----   1 root disk       43,   2 Mar 30 14:51 nbd0p2
mytux:/vmw/Win7Test # fdisk -l /dev/nbd0
Disk /dev/nbd0: 4 GiB, 4294967296 bytes, 8388608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0efb9e3e

Device      Boot   Start     End Sectors  Size Id Type
/dev/nbd0p1         2048 5312511 5310464  2.5G  7 HPFS/NTFS/exFAT
/dev/nbd0p2      5312512 8384511 3072000  1.5G  7 HPFS/NTFS/exFAT
mytux:/vmw/Win7Test # # 

Das, was hier so selbstverständlich aussieht, ist angesichts der Komplexität des vmdk-Formats eigentlich ein kleines Wunder. Man beachte, dass hier keine irgendwie geartete Kopie der vmdk-Disk in einem neuen Format erzeugt wurde. Vielmehr arbeiten wir auf den originalen Daten – deren Adressierung über eine Block-Layer-Schicht vermittelt wird. Dafür setzt qemu-nbd meines Wissens auch nicht FUSE ein (unix.stackexchange.com-questions-192875/qemu-nbd-vs-vdfuse-for-mounting-vdi-images)

Schritt 3 – Mounten:
So, wie wir qemu-nbd hier verwendet haben, ist es sehr zuvorkommend zu uns und weist neben dem Block-Device “/dev/nbd0” für die gesamte Disk gleich auch noch weitere Block-Devices für die intern erkannten Partitionen des Disk-Images aus. Letztere können wir direkt mounten:

nmytux:/vmw/Win7Test # mount /dev/nbd0p2 /mnt2
mytux:/vmw/Win7Test # la /mnt2
total 196128
drwxrwxrwx  1 root root         0 Mar 28 09:52 $RECYCLE.BIN
drwxrwxrwx  1 root root      4096 Mar 28 20:25 .
drwxr-xr-x 39 root root      4096 Mar 28 17:02 ..
drwxrwxrwx  1 root root      4096 Mar 28 09:53 Cosmological_Voids
drwxrwxrwx  1 root root         0 Mar 28 20:26 Muflons
drwxrwxrwx  1 root root         0 Mar 28 09:51 System Volume Information
-rwxrwxrwx  2 root root 200822784 Nov  4  2013 mysql-installer-community-5.6.14.0.msi
drwxrwxrwx  1 root root         0 Mar 29 10:37 ufos

Zugriffs-Rechte und deren Abänderung

Ähnlich wie bei “vmware-mount”, das wir im ersten Artikel dieser Serie behandelt hatten, bekommen wir je nach Zweck der vmdk-Untersuchung ggf. ein Problem mit Rechten – siehe die durchgehenden 777-Rechte-Kämme nach dem Mounten. Im Fall von qemu-nbd können wir das aber rechtzeitig im Zuge des Mountens korrigieren.

Dabei ist – je nach Untersuchungszweck – die Frage zu stellen: Wer soll welche Art von Zugriff erhalten und wie privilegiert soll derjenige sein? Ich zeige mal 2 Varianten. (Für das genauere Verständnis sollte man sich mit Mount-Optionen und umasks bzw. fmasks und dmasks befassen.)

Variante 1: Nur Root soll rein lesenden Zugang erhalten:

mytux:/vmw/Win7Test # mount -o uid=root,gid=root,umask=0277 /dev/nbd0p2 /mnt2
mytux:/vmw/Win7Test # la /mnt2
total 196128                                                                                                                        
dr-x------  1 root root         0 Mar 28 09:52 $RECYCLE.BIN/                                                                        
dr-x------  1 root root      4096 Mar 28 20:25 ./                                                                                   
drwxr-xr-x 39 root root      4096 Mar 28 17:02 ../
dr-x------  1 root root      4096 Mar 28 09:53 Cosmological_Voids/
dr-x------  1 root root         0 Mar 28 20:26 Muflons/
dr-x------  1 root root         0 Mar 28 09:51 System Volume Information/
-r-x------  2 root root 200822784 Nov  4  2013 mysql-installer-community-5.6.14.0.msi*
dr-x------  1 root root         0 Mar 29 10:37 ufos/

Variante 2: Nur der User “myself” soll lesenden und schreibenden Zugang zu Dateien und Directories des gemounteten NTFS-Systems erhalten:

mytux:/vmw/Win7Test # mount -o uid=rmx,gid=users,fmask=0177,dmask=0077 /dev/nbd0p2 /mnt2
mytux:/vmw/Win7Test # la /mnt2
total 196128
drwx------  1 myself  users         0 Mar 28 09:52 $RECYCLE.BIN
drwx------  1 myself  users      4096 Mar 28 20:25 .
drwxr-xr-x 39 root root       4096 Mar 28 17:02 ..
drwx------  1 myself  users      4096 Mar 28 09:53 Cosmological_Voids
drwx------  1 myself  users         0 Mar 28 20:26 Muflons
drwx------  1 myself  users         0 Mar 28 09:51 System Volume Information
-rw-------  2 myself  users 200822784 Nov  4  2013 mysql-installer-community-5.6.14.0.msi
drwx------  1 myself  users         0 Mar 29 10:37 ufos
mytux:/vmw/Win7Test # 

Die Linux-Rechte sagen allerdings wenig darüber aus, wem ggf. neu angelegte Dateien mit welchen Rechten dann später welchem User auf einem virtuellen Windows gehören würden. Siehe zu dieser Thematik den entsprechenden Abschnitt und zugehörige Links im ersten Artikel der Serie.

Unmounten und Entfernen der Beziehung eines nbd-Devices zum Disk-Image

Nachdem man die Untersuchung einer Partition des vmdk-Disk-Images unter Linux abgeschlossen hat, muss man alles wieder rückgängig machen. Das geht wie folgt:

mytux:/vmw/Win7Test # umount /mnt2
mytux:/vmw/Win7Test # qemu-nbd -d /dev/nbd0 
/dev/nbd0 disconnected
mytux:/vmw/Win7Test # rmmod nbd
mytux:/vmw/Win7Test # 

Den umount-Befehl muss
man natürlich für alle ggf. gemounteten Partitionen absetzen.

Nutzung von Loop-Devices?

nbd war deshalb sehr hilfsbereit, weil wir beim Laden des Kernelmoduls vorgegeben hatten, wieviele Partitionen maximal verwaltet werden sollen. Frage: Können wir die Partitionen auch anders bekommen, wenn wir etwa den Parameter des Kernelmoduls weglassen? Antwort: Ja, das geht.

Ich möchte zwei Varianten vorstellen, die sich nicht nur auf nbd-Devices, sondern in gleicher Weise auch auf raw- oder flat-Files, die man etwa als Output von “vmware-mount -f” erhalten würde, anwenden lassen.

Die erste Methode nutzt Loop-Devices. Loop- oder Loopback-Devices kennt der Linux-Anwender normalerweise im Zusammenhang mit mit der Nutzung von Filesystemen, die von Raw-Dateien beherbergt werden. Man vergisst dabei oft, dass sich Loop-Devices auch Block-Devices überstülpen lassen; die man-Page zu “losetup” sagt dazu:

DESCRIPTION
losetup is used to associate loop devices with regular files or block devices, to detach loop devices, and to query the status of a loop device.

Letztlich ist unter Linux/Unix halt alles ein File :-). Also:

mytux:/vmw/Win7Test # modprobe nbd
mytux:/vmw/Win7Test # qemu-nbd -c /dev/nbd0 Win7_x64_ssdx-000002.vmdk 
mytux:/vmw/Win7Test # la /dev | grep nbd0
brw-rw----   1 root disk       43,   0 Mar 31 15:19 nbd0

mytux:/vmw/Win7Test # fdisk -l /dev/nbd0
Disk /dev/nbd0: 4 GiB, 4294967296 bytes, 8388608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0efb9e3e

Device      Boot   Start     End Sectors  Size Id Type
/dev/nbd0p1         2048 5312511 5310464  2.5G  7 HPFS/NTFS/exFAT
/dev/nbd0p2      5312512 8384511 3072000  1.5G  7 HPFS/NTFS/exFAT

Nun müssen wir noch die Offsets der Partitionen in Bytes aus den Start und End-Sektoren berechnen:

Offset Partition 1: 2048 * 512 = 1048576
Offset Partition 2: 5312512 * 512 = 2720006144

Diese Offset-Positionen sind dann über die Option “-o” im losetup-Kommando anzugeben:

mytux:/vmw/Win7Test # losetup -o 1048576 /dev/loop1 /dev/nbd0
mytux:/vmw/Win7Test # losetup -o 2720006144 /dev/loop2 /dev/nbd0
mytux:/vmw/Win7Test # mount /dev/loop1 /mnt2
mytux:/vmw/Win7Test # mount /dev/loop2 /mnt3
mytux:/vmw/Win7Test # la /mnt2
total 8
drwxrwxrwx  1 root root    0 Mar 27 19:35 $RECYCLE.BIN
drwxrwxrwx  1 root root 4096 Mar 27 19:35 .
drwxr-xr-x 39 root root 4096 Mar 28 17:02 ..
drwxrwxrwx  1 root root    0 Mar 27 19:34 System Volume Information

mytux:/vmw/Win7Test # touch /mnt2/hallo.txt
mytux:/vmw/Win7Test # la /mnt2
total 8
drwxrwxrwx  1 root root    0 Mar 27 19:35 $RECYCLE.BIN
drwxrwxrwx  1 root root 4096 Mar 31 15:24 .
drwxr-xr-x 39 root root 4096 Mar 28 17:02 ..
drwxrwxrwx  1 root root    0 Mar 27 19:34 System Volume Information
-rwxrwxrwx  1 root root    0 Mar 31 15:24 hallo.txt

mytux:/vmw/Win7Test # la /mnt3
total 196128
drwxrwxrwx  1 root root         0 Mar 28 09:52 $RECYCLE.BIN
drwxrwxrwx  1 root root      4096 Mar 28 20:25 .
drwxr-xr-x 39 root root      4096 Mar 28 17:02 ..
drwxrwxrwx  1 root root      4096 Mar 28 09:53 Cosmological_Voids
drwxrwxrwx  1 root root         0 Mar 28 20:26 Muflons
drwxrwxrwx  1 root root         0 Mar 28 09:51 System Volume Information
-rwxrwxrwx  2 root root 200822784 Nov  4  2013 mysql-installer-community-5.6.14.0.msi
drwxrwxrwx  1 root root         0 Mar 31 14:02 ufos
mytux:/vmw/Win7Test # 
mytux:/vmw/Win7Test # cat /mnt3/ufos/ufo.txt
Ufos are not real
mytux:/vmw/Win7Test #

Zurückdrehen können wir den gesamten Prozess wie folgt:

mytux:/vmw/Win7Test # umount /mnt3
mytux:/vmw/Win7Test # umount /mnt2
mytux:/vmw/Win7Test # losetup -d /dev/loop2
mytux:/vmw/Win7Test # losetup -d /dev/loop1
mytux:/vmw/Win7Test # qemu-nbd -d /dev/nbd0 
/dev/nbd0 disconnected
mytux:/vmw/Win7Test # modprobe -r nbd
mytux:/vmw/Win7Test # 

Loop-Devices und “vmware-mount -f”
Das Ganze klappt natürlich auch mit “vmware-mount -f” und dem dadurch erzeugten “flat”-File (s. den ersten Artikel):

mytux:/vmw/Win7Test # vmware-mount -f Win7_x64_ssdx-000002.vmdk /mnt/vmdk
mytux:/vmw/Win7Test # la /mnt/vmdk
total 4194304
-rw------- 1 myself users 4294967296 Mar 31 15:25 flat
mytux:/vmw/Win7Test # losetup -o 2720006144 /dev/loop2 /mnt/vmdk/flat 
mytux:/vmw/Win7Test # mount /dev/loop2 /mnt3
mytux:/vmw/Win7Test # cat /mnt3/ufos/ufo.txt 
Ufos are not real
mytux:/vmw/Win7Test # umount /mnt3
mytux:/vmw/Win7Test # losetup -d /dev/loop2
mytux:/vmw/Win7Test # vmware-mount -d /mnt/vmdk
mytux:/vmw/Win7Test #

Über diesen Weg können wir übrigens auch das Problem mit den Zugriffsrechten lösen, dass wir im ersten Artikel für “vmware_mount” angesprochen hatten – wir legen die Rechte analog zum oben besprochenen Vorgehen im Zuge des mount-Befehls fest.

kpartx?

Erfahrene Linux-User wissen, dass kpartx ein nettes Tool ist, das aus Block-Devices oder Raw-Files evtl. enthaltene Partitionstabellen ermittelt und über “/dev/mapper” entsprechende Devices bereitstellt. Das funktioniert natürlich auch auf Basis von “/dev/nbdX”-Devices:

mytux:/vmw/Win7Test # modprobe nbd
mytux:/vmw/Win7Test # qemu-nbd -c /dev/nbd0 Win7_x64_ssdx-000002.vmdk 
mytux:/vmw/Win7Test # kpartx -av /dev/nbd0 
add map nbd0p1 (254:12): 0 5310464 linear 43:0 2048
add map nbd0p2 (254:13): 0 3072000 linear 43:0 5312512    
mytux:/vmw/Win7Test # la /dev/mapper | grep nbd0
lrwxrwxrwx  1 root root       8 Mar 31 15:58 nbd0p1 -> ../dm-12
lrwxrwxrwx  1 root root       8 Mar 31 15:53 nbd0p2 -> ../dm-13
mytux:/vmw/Win7Test # mount /dev/dm-13 /mnt3
mytux:/vmw/Win7Test # cat /mnt3/ufos/ufo.txt
Ufos are not real
mytux:/vmw/Win7Test # umount /mnt3
mytux:/vmw/Win7Test # kpartx -d /dev/nbd0
mytux:/vmw/Win7Test # la /dev/mapper | grep nbd0
mytux:/vmw/Win7Test # modprobe -r nbd
mytux:/vmw/Win7Test # 

Analog für ein flat-File von vmware-mount:

mytux:/vmw/Win7Test # vmware-mount -f Win7_x64_ssdx-000002.vmdk /mnt/vmdk
mytux:/vmw/Win7Test # kpartx -av /mnt/vmdk/flat 
add map loop0p1 (254:12): 0 5310464 linear 7:0 2048
add map loop0p2 (254:13): 0 3072000 linear 7:0 5312512
mytux:/vmw/Win7Test # mount /dev/dm-13 /mnt3
mytux:/vmw/Win7Test # cat /mnt3/ufos/ufo.txt
Ufos are not real
mytux:/vmw/Win7Test # umount /mnt3
mytux:/vmw/Win7Test # kpartx -d /mnt/vmdk/flat
loop deleted : /dev/loop0
mytux:/vmw/Win7Test # vmware-mount -d /mnt/vmdk
mytux:/vmw/Win7Test # 

Alles gut !

Read-Only-Option?

In allen oben dargestellten Beispielen haben wir bislang durchgehend rw-Mounts durchgeführt. Da ist bei vielen Analysen nicht erwünscht. Grundsätzlich ist im Umgang mit Partitionen regelmäßig Vorsicht angebracht, um nichts zu zerstören. Write-´Zugriffe sollte man immer zuerst auf Kopien testen.

Daher stellt sich die Frage nach einer “ro”-Option von “qemu-nbd”. Die gibt es, sie lautet “-r”. Deren Setzung schlägt auf alle weiteren Maßnahmen durch:

mytux:/vmw/Win7Test # modprobe nbd max-part=4
mytux:/vmw/Win7Test # qemu-nbd -r -c /dev/nbd0 Win7_x64_ssdx-000002.vmdk 
mytux:/vmw/Win7Test # mount /dev/nbd0p2 /mnt3
fuse: mount failed: Permission denied
mytux:/vmw/Win7Test # mount -o ro /dev/nbd0p2 /mnt3
mytux:/vmw/Win7Test # 

Gibt es Größenlimit für die virtuelle Disk?

Ehrlich gesagt: keine Ahnung. Wenn es ein aktuelles Limit gibt, würde ich aufgrund älterer Infos im Zusammenhang mit nbd auf 1TB tippen. Wenn jemand was Genaueres weiß, kann er mir ja eine Mail schreiben. Siehe auch:

vsphere-50 und vddk
vddk51_programming.pdf

Fazit und Ausblick

Das nbd-Kernelmodul und das Kommando qemu-nbd eröffnen relativ einfache Zugänge zu komplexen vmdk-Image-Dateien. Dabei wird auch die aktuelle Version 6 des vmdk-Formats beherrscht. Wir haben zudem die Möglichkeit, das Mounten der bereitgestellten nbd-Block-Devices bzgl. der Rechte individuell zu gestalten.

Im nächsten Artikel

Mounten eines vmdk-Laufwerks im Linux Host – IV – guestmount, virt-filesystems, qemu-img

gehe ich kurz auf “qemu-img” ein und betrachte dann das Kommando “guestmount” aus der neueren und Fuse-basierten Toolkiste von “libguestfs”.

Links

losetup – Abkürzung für bestimmte mknod-Operationen zur Nutzung von Loop-Devices
unix.stackexchange.com/questions/98742/how-to-add-more-dev-loop-devices-on-fedora-19

qemu-nbd
https://wiki.ubuntuusers.de/QEMU/
https://opsech.io/posts/2017/Jun/07/how-to-mount-vm-disk-images-on-fedora.html
http://blog.vmsplice.net/2011/02/how-to-access-virtual-machine-image.html
https://sweetcode.io/introduction-to-linux-network-block-devices/

Mounten eines vmdk-Laufwerks im Linux Host – II – Einschub, Spezifikation, Begriffe

Diese Artikelserie befasst sich mit Linux-Kommandos, die den Zugriff auf Inhalte von Image-Files für virtuelle vmdk-Disks erlauben. Den Lesern meines letzten Artikels zum Kommando “vmware-mount”

Mounten eines vmdk Laufwerks im Linux Host – I – vmware-mount

möchte ich in diesem Artikel ein paar Links zur vmdk-Spezifikation nachreichen. Dabei wird einerseits nochmals die Komplexität des vmdk-Formats deutlich, mit denen Linux-Tools sich auseinandersetzen müssen. Andererseits möchte ich im Vorgriff auf weitere Artikel ein paar Begrifflichkeiten glattziehen und die Nomenklatur von “sprechenden” Zusätzen in vmdk-Datei-Namen erläutern. Dieser Einschub war nicht geplant; er erschien mir aber u.a. sinnvoll, um zu verdeutlichen, warum bestimmte Linux-Tools, mit denen wir uns noch beschäftigen werden, ernsthafte Problem mit bestimmten vmdk-Varianten haben.

vmdk-Spezifikationen

Das vmdk-Format ist ziemlich komplex und hat sich im Lauf der Zeit auch mehrfach geändert. Das Format nimmt auf unterschiedliche Bedürfnisse Rücksicht, die im Zusammenhang mit Virtualisierung und zugehörigen Image-Files für virtuelle Disks auf Virtualisierungshosts auftreten. Zu nennen sind diesbzgl.: Platzbedarf versus Performance; Zustandssicherung von virtuellen Maschinen durch Snapshots und zugehörige Delta-Informationen; unterschiedliche Lagerstätten von vmdk_Files im Verzeichnisbaum.

Den aktuellen Stand spiegeln Informationen unter folgenden Links wieder:

vmdk-spec-in-ascii-doc-format
VMware PDF zu Virtual Disk Format 5.0
https://www.vmware.com/app/vmdk/?src=vmdk
ttgtmedia-PDF zum Umgang mit vmdk

Einige weitere interessante Infos liefert auch die API-Beschreibung für Entwickler:
https://vdc-download.vmware.com/vmwb-repository/dcr-public/ae1a858c-2f59-4a75-b333-f2ddfa631fbd/4ae542fa-c7ec-4713-a3a0-6fef938b03d1/vddk60_programming.pdf

Ich kann euch das Lesen nicht abnehmen, nachfolgend gehe ich aber auf Punkte ein, die die Vielzahl und die unterschiedlichen Bezeichnungen von vmdk-Dateien, deren Anzahl sich unter bestimmten Voraussetzungen im Lauf der Zeit beträchtlich erhöhen kann, erklären.

Vielfalt – schon bei einer einzelnen virtuellen Maschine

Im Hauptverzeichnis zu einer meiner virtuellen Testmaschinen finden sich etwa folgende vmdk-Dateien:

mytux:/vmw/WinTest # ls | grep vmdk
Win7_x64-cl1-000001-s001.vmdk
Win7_x64-cl1-000001-s002.vmdk
...
Win7_x64-cl1-000001-s016.vmdk
Win7_x64-cl1-000001-s017.vmdk
Win7_x64-cl1-000001.vmdk
Win7_x64-cl1-000002-s001.vmdk
Win7_x64-cl1-000002-s002.vmdk
...
Win7_x64-cl1-000002-s016.vmdk
Win7_x64-cl1-000002-s017.vmdk
Win7_x64-cl1-000002.vmdk
Win7_x64-cl1-000003-s001.vmdk
Win7_x64-cl1-000003-s002.vmdk
...
Win7_x64-cl1-000003-s016.vmdk
Win7_x64-cl1-000003-s017.vmdk
Win7_x64-cl1-000003.vmdk
Win7_x64-cl1-000004-s001.vmdk
Win7_x64-cl1-000004-s002.vmdk
...
Win7_x64-cl1-000004-s016.vmdk
Win7_x64-cl1-000004-s017.vmdk
Win7_x64-cl1-000004.vmdk
Win7_x64-cl1-s001.vmdk
Win7_x64-cl1-s002.vmdk
...
Win7_x64-cl1-s016.vmdk
Win7_x64-cl1-s017.vmdk
Win7_x64-cl1-s018.vmdk
Win7_x64-cl1.vmdk
Win7_x64_ssd_ex-000001.
vmdk
Win7_x64_ssd_ex-000002.vmdk
Win7_x64_ssd_ex-000003.vmdk
Win7_x64_ssdx-000001-s001.vmdk
Win7_x64_ssdx-000001-s002.vmdk
Win7_x64_ssdx-000001.vmdk
Win7_x64_ssdx-000002-s001.vmdk
Win7_x64_ssdx-000002-s002.vmdk
Win7_x64_ssdx-000002.vmdk

 
Das ist schon eine erstaunliche Vielfalt; dabei habe ich in der Liste bereits viele Dateien (u.a. sog. Extents mit Namenszusätzen “s003” bis “s015”) ausgeblendet. Um die Lage noch zu verkomplizieren, liegen einige Basisdateien zu Testzwecken auch noch in in einem anderen Verzeichnis (nämlich /vmw/Win7).

Hinweis: VMware legt Snapshots defaultmäßig immer im Verzeichnis der virtuellen Maschine an; die Ursprungsdateien für Disk-Images können sich jedoch an anderer Stelle im Verzeichnisbaum befinden.

Hinweise und ein paar Zitate aus der Spezifikation

Die ersten drei der oben genannten Dokumente legen u.a. fest, dass die reinen vmdk-Dateien, also diejenigen ohne “-snnn”-Anteil im Namen, sogenannte “Deskriptor”-Dateien sind. Meine Leser kennen den Begriff schon vom vorhergehenden Artikel. Auch das nächste Zitat belegt (bis auf den letzten Satz) einige Punkte, die ich bereits früher angedeutet hatte:

“VMware virtual disks can be described at a high level by looking at two key characteristics:
     * The virtual disk may use backing storage contained in a single file, or it may use storage that consists of a collection of smaller files.
    * All of the disk space needed for a virtual disk’s files may be allocated at the time the virtual disk is created, or the virtual disk may start small and grow as needed to accommodate new data.
A particular virtual disk may have any combination of these two characteristics.
One characteristic of recent ‐ generation VMware virtual disks is that a text descriptor describes the layout of the data in the virtual disk. This descriptor may be saved as a separate file or may be embedded in a file that is part of a virtual disk.”

Ich nenne in dieser Artikelserie ein Disk-Image, das sich über mehrere Dateien erstreckt, auch einen “Container“. In der Reihe der Dateien, die gemäß vmdk-Spezifikation zu einem Container gehören, ist zu unterscheiden zwischen

  • der Deskriptor-Datei
  • den durchnummerierten “Extent”-Dateien.

Das erste Extent-File bezeichne ich auch als “Base-File” (nicht zu verwechseln mit der “Base-Disk”; s.u.)

Das vierte der oben per Link angegebenen Dokumente weist dabei auf Folgendes hin (eine noch präzisere Info zu den verschiedenen Erscheinungsformen von vmdk-Disk-Images liefert die API-Doku):

“The VMDK file can have four different forms. Type 0 (monolithic sparse disk), Type 1 (growable; split into 2GB files), Type 2 (single pre-allocated; monolithic sparse disk), and Type 3 (pre-allocated; split into 2GB files). Types 1, 2, and 3 use a disk descriptor file, while type 0 does not.
To make changes to the VMDK file, you need to be able to open and view the disk descriptor; otherwise, with the type 0 single disk, you would need to edit a very large binary file with a hex editor – an unwise choice. A better option, if you have the VMDK file on a VMFS file system, is to use vmkfstools to easily export the file in a Type 3 format.”

Hinweis:

Obwohl hier 2GB als Standardgröße der Extension-Files einer “growable, sparse Disk” angegeben werden, werden u.a. von der VMware-Workstation unter Linux inzwischen standardmäßig 4GB große Dateien für Extent-Dateien erzeugt.

Die Komplexität wird durch Snapshots noch weiter gesteigert: Die die
vmdk-Spezifikation erklärt Snapshots über Dateien (“Links”) in einer Link-Kette, bei der Delta-Files auf logisch vorhergehende Delta-Files und zuletzt auf Files der ursprüngliche “Base”-Disk verweisen – und zwar nach folgendem Muster:

Delta-Dateien zu Snapshot N   =>   Delta-Dateien zu Snapshot N-1   =>   …   =>   Delta-Dateien zu Snapshot 1   =>   Dateien der Base-Disk

Eine Base-Disk kann als “growable Disk” angelegt worden sein. Sie und auch zugehörige Snapshots weisen dann ein Base-File und weitere Extents auf – in den Snapshots sind die jeweiligen Dateien dann aber nur mit Delta-Informationen zum jeweiligen Extent gefüllt.

Wie schlägt sich die Komplexität in den Namens-Zusätzen der vmdk-Files nieder?

Eine interessante Frage, mit der man sich ggf. auseinandersetzen muss, ist, wie sich die mögliche Struktur in der Namensgebung von vmdk-Dateien widerspiegelt. Dazu gibt es – zumindest bzgl. der VMware Workstation – vier Regeln:

  • Eine Datei, die durch Clonen einer Maschine mit VMware-Tools entstanden ist, erhält einen Zusatz “-clN”. N steht dabei für die Nummer des Clones.
  • Ein Snapshot, der mit VMware-Tools angelegt wurde, erhält einen Zusatz in Form einer 6-stelligen Nummer, z.B. “-000024”. Im Beispiel wäre das der 24-te Snapshot.
  • Eine Deskriptor-Datei enthält außer dem Clone- und dem Snapshot-Zusatz keine weiteren Zusätze im Namen.
  • Eine neue Extend-Datei erhält in aufsteigender Nummerierung einen Zusatz “-sNNN”; dabei steht “NNN” für eine dreistellige Zahl – z.B. “-s017”. Das Beispiel würde das File für den 17-ten Extent markieren. Ein erster Extent (“-s001”) wird bei einer growable sparse Disk immer angelegt; das zugehörige File ist das von mir als solches bezeichnete Base-File.

Dabei gilt nicht zwingend, dass die Snapshot-Nummer für alle Disk-Images einer virtuellen Maschine immer gleich sein muss. Eine Disk könnte der Maschine ja z.B. erst nach dem Snapshot 43 hinzugefügt worden sein. Pro Disk wird ab dem ersten zugehörigen Snapshot-Event hochnummeriert: Die Dateien zur neuen Disk erhalten dann beim zweiten nachfolgenden Snapshot “-000002” als Zusatz, während bei älteren Disks der Zähler schon auf “-000045” steht.

Blick auf eine Deskriptor-Datei

Nun könnten wir uns ja einfach mal eine Descriptor-Datei ansehen. Aber Vorsicht bei “monolithic sparse disks” (Typ 0)! Die Shell (cat-Kommando) und viele einfache Editoren sind keine Hex-Editoren! “Typ 0” ist aber gerade für unsere Test-Disk, die durch “/vmw/WinTest/Win7_x64_ssd_ex-000003.vmdk” beschrieben wird, gegeben. Problemfreier sollte sich die Deskriptor-Datei dagegen für eine andere zweite vmdk-Disk, die als Container mit 2 “sparse extends” (Typ 1) angeboten wird, auslesen lassen. Probieren wir das mal mit dem Deskriptor File “Win7_x64_ssdx-000002.vmdk” des für diese Disk vorliegenden zweiten Snapshots aus:

mytux:/vmw/Win7Test # cat Win7_x64_ssdx-000002.vmdk 
# Disk DescriptorFile
version=1
encoding="UTF-8"
CID=a3a5e6df
parentCID=d135caf6
createType="twoGbMaxExtentSparse"
parentFileNameHint="Win7_x64_ssdx-000001.vmdk"
# Extent description
RW 8323072 SPARSE "Win7_x64_ssdx-000002-s001.vmdk"
RW 65536 SPARSE "Win7_x64_ssdx-000002-s002.vmdk"

# The Disk Data Base 
#DDB

mytux:/vmw/Win7Test # cat Win7_x64_ssdx-000001.vmdk 
# Disk DescriptorFile
version=1
encoding="UTF-8"
CID=d135caf6
parentCID=b665cd6b
createType="twoGbMaxExtentSparse"
parentFileNameHint="/vmw/Win7/Win7_x64_ssdx.vmdk"
# Extent description
RW 8323072 SPARSE "Win7_x64_
ssdx-000001-s001.vmdk"
RW 65536 SPARSE "Win7_x64_ssdx-000001-s002.vmdk"

# The Disk Data Base 
#DDB

ddb.longContentID = "d10b249e54a5cd42df5ab314d135caf6"
mytux:/vmwssd_w7prod/Win7_x64 # 

Was lernen wir?

  • In der Deskriptor-Datei sind keine Informationen enthalten, die die Partitionsstruktur der Disk-Images offenbaren würden.
  • Die Extents sind als wachsende “sparse”-Dateien konfiguriert.
  • Die Verlinkung erfolgt über CIDs und Pfadangaben.
  • Die Base-Disk wurde im vorliegenden Fall in einem anderen Verzeichnis definiert als dem der virtuellen Maschine.

Interne Daten-Adressierung und Partitionsinformation

Die vmdk-Spezifikation beschreibt relativ detailliert, wie die die Block-Adressierung der wachsenden Daten systematisch über zugehörige Informationen

  • in Headern von Extent-Files,
  • in einem sog. Grain-Directory des Extents
  • und zugehörigen Grain-Tabellen mit Einträgen zu Offsets für konkret definierte “Grains” (Block of Sectors, GrainSize*SectorSize, z.B. 128 * 512 = 64KB)

realisiert und gesteuert wird. Ferner gilt, dass die Deskriptor-Information in Extent Files selbst enthalten sein kann.

Die Partitionsinformation liegt bei einer normalen Platte in der Partitionstabelle (MBR, GPT-Format). Diese befindet sich ziemlich am Anfang einer Platte. Daraus folgern wir, dass dem Erkennen des Basis-Files eines “sparse/growable”-Disk-Images und der Analyse dieses Files (neben der Deskriptor-Datei) große Bedeutung zukommt.

Runterbrechen auf ein Flat- oder Raw-File?

Der Einsatz von Loop-Devices unter Linux für den Zugriff auf Partitionen, die in Disk-Images verborgen werden, erfordert ein spezielles “flaches” Format einer zusammenhängenden Datei. Letztere wird manchmal als “raw”- oder “flat”-File bezeichnet.

Da vmdk-Disks wegen Snapshots und Extents vielfach in Einzeldateien gesplittet und letztere wiederum untereinander verlinkt sind, muss ein solches zusammenhängendes “raw”-File erstmal durch Analyse der Dateibeziehungen, der Adressierung von virtuellen Datenblöcken innerhalb einer Datei und durch Auswertung von Delta-Informationen erzeugt werden.

Das erledigen viele Tool-Entwickler, indem sie FUSE-Mechanismen nutzen.

Alternativ kann man (über FUSE) auch andere Schritte gehen – und das Filesystem direkt mit dem Host-Kernel oder dem zwischengeschobenen Kernel einer (verdeckten) virtuellen Maschine kommunizieren lassen (siehe hierzu etwa
http://libguestfs.org/guestfs-security.1.html. Generell ist übrigens zu sagen, dass das direkte Mounten eines Filesystems aus einer ggf. manipulierten virtuellen Maschine über Loop-Devices durchaus mit Gefahren verbunden sein kann.)

Da vmdk so komplex ist, wundert es nicht, dass etliche im Internet besprochene Linux-Tools wie affuse, vdfuse und z.T. auch vmdkmount in ihrem aktuellen Zustand an der korrekten Aufdröselung von Snapshots oder aber Extents scheitern. Zumindest nach meiner aktuellen Erfahrung.

Runterbrechen auf ein Block-Device?

Alternativ zu einem Flat-File kann ein Tool auch ein “künstliches” Block-Device für das Disk-Image bereitstellen. Dazu muss – ähnlich wie bei echten HW-Disk-Treibern – ein “künstliches” Device mit einem entsprechenden Block-Layer durch ein Kernel-Modul, das die korrekte Adressierung leistet, bereitgestellt und verwaltet werden.

Zusammenfassung / Ausblick

vmdk-Images sind komplex; sie

  • vdmk-Disk-Images erlauben Snapshots – d.h. die Verlinkung von Delta-Files.
  • Ein vdmk-Disk-Image kann als Container
    angelegt werden, der sich über viele Einzeldateien (Extends) erstreckt.
  • Das Disk-Image kann so angelegt werden, dass die Zahl der Extents (und zugehöriger Dateien) im Lauf der Zeit bedarfsgerecht wächst. Jeder Extent wächst dabei dynamisch auf sein Limit an.
  • Ein vdmk-Disk-Image kann alternativ als eine sukzessive wachsende Einzeldatei für ein Disk-Image angelegt werden.
  • Ein vdmk-Disk-Image erlaubt eine einmalige, vollständige Platz-Allokierung für die gesamte Disk.
  • Pro vdmk-Disk-Image sind mehrere Partitionen/Filesysteme erlaubt.

Davon treten ggf. mehrere Punkte in Kombination auf. Das stellt Linux-Tools u.a. vor das Problem, wie man interessierten Usern entweder

  • eine flat- oder raw-Datei zur Verfügung stellen will, die der User dann selbst – z.B. über Loop-Devices – weiterverarbeitet,
  • einen linux-fähigen Block-Layer über die vmdk-Adressierung legen,
  • oder mittels FUSE gleich einen anderen, ggf. direkteren Zugang zu den enthaltenen Partitionen eines vmdk-Disk-Images liefert.

Im nächsten Artikel

Mounten eines vmdk-Laufwerks im Linux Host – III – qemu-nbd, loop-devices, kpartx

bespreche ich zunächst ein Linux-Tool, das den zweiten der genannten Wege einschlägt und dabei auch Snapshots und Extents beherrschen – nämlich qemu-nbd