Lo hice y lo entendí

El blog de Vicente Navarro
05 oct

Compactar imágenes de disco de VirtualBox: vditool vs VBoxManage

En Probar en VirtualBox una memoria USB de arranque obtuvimos una imagen vdi de una imagen que habíamos obtenido con dd de una memoria USB de arranque para simular su funcionamiento sin tener que reinciar una y otra vez.

La imagen que creamos allí era, por defecto, de tamaño fijo (no dinámico), de modo que ocupaba en el disco duro el mismo espacio del que disponía la memoria USB, en mi caso 8 GB (que no 8 GiB):

-rwxr-xr-x  1 root    root    8000004096 2008-09-26 23:41 imagen_mem_usb.img*
-rw-------  1 vicente vicente 8000666112 2008-10-05 18:57 imagen_mem_usb.vdi*

Como vimos en Montar imágenes de disco vdi de VirtualBox, podemos convertir dicha imagen de tamaño fijo en una de tamaño dinámico y además, eliminando los bloques no usados, con “vditool SHRINK“:

$ vditool SHRINK imagen_mem_usb.vdi 
vditool    Copyright (c) 2004-2008 innotek GmbH.

Shrinking VDI image file="imagen_mem_usb.vdi"...
progress: 0%Log created: 2008-09-27T12:19:10.484950000Z
Executable: /usr/lib/virtualbox/vditool
Arg[0]: /usr/lib/virtualbox/vditool
Arg[1]: SHRINK
Arg[2]: imagen_mem_usb.vdi
Dumping VDI image "imagen_mem_usb.vdi" mode=r/w fOpen=0 File=00000004
Header: Version=00010001 Type=2 Flags=0 Size=8000004096
Header: cbBlock=1048576 cbBlockExtra=0 cBlocks=7630 cBlocksAllocated=7630
Header: offBlocks=512 offData=31232
Header: Geometry: C/H/S=15501/16/63 cbSector=512 Mode=2
Header: uuidCreation={53bea69e-c21b-4273-be9e-df3fa8f1ffd7}
Header: uuidModification={7a7be4b9-188a-42b2-298e-07eaa266a651}
Header: uuidParent={00000000-0000-0000-0000-000000000000}
Header: uuidParentModification={00000000-0000-0000-0000-000000000000}
Image:  fFlags=00000000 offStartBlocks=512 offStartData=31232
Image:  uBlockMask=000FFFFF uShiftIndex2Offset=20 uShiftOffset2Index=20 offStartBlockData=0
...........10%..........20%..........30%..........40%..........50%
..........60%..........70%..........80%..........90%..........100%
Dumping VDI image "imagen_mem_usb.vdi" mode=r/w fOpen=0 File=00000004
Header: Version=00010001 Type=2 Flags=0 Size=8000004096
Header: cbBlock=1048576 cbBlockExtra=0 cBlocks=7630 cBlocksAllocated=7621
Header: offBlocks=512 offData=31232
Header: Geometry: C/H/S=15501/16/63 cbSector=512 Mode=2
Header: uuidCreation={53bea69e-c21b-4273-be9e-df3fa8f1ffd7}
Header: uuidModification={7a7be4b9-188a-42b2-298e-07eaa266a651}
Header: uuidParent={00000000-0000-0000-0000-000000000000}
Header: uuidParentModification={00000000-0000-0000-0000-000000000000}
Image:  fFlags=00000000 offStartBlocks=512 offStartData=31232
Image:  uBlockMask=000FFFFF uShiftIndex2Offset=20 uShiftOffset2Index=20 offStartBlockData=0

The operation completed successfully!

Sin embargo, en este caso no conseguimos apenas ningún ahorro de espacio:

-rwxr-xr-x  1 root    root    8000004096 2008-09-26 23:41 imagen_mem_usb.img*
-rw-------  1 vicente vicente 7993326080 2008-09-30 21:04 imagen_mem_usb.vdi*

La imagen compactada es tan sólo 8000666112-7993326080=7340032 bytes más pequeña que la anterior. Esto es debido a que el proceso de compactación de vditool sólo es capaz de ahorrarnos los bloques que contengan íntegramente ceros, y en el caso de mi memoria USB, fruto de innumerables movimientos de ficheros, ya vemos que son muy pocos los bloques que continuaban conteniendo exclusivamente ceros. Es bien sabido que cuando borramos un fichero, su contenido sigue estando íntegramente en el disco y sólo anotamos en las estructuras del sistema de ficheros que ese espacio que antes usaba tal fichero, ahora está disponible para cualquier otro fichero. Y eso es en lo que se basan los programas de recuperación de ficheros eliminados para hacer su trabajo con mayor o menor éxito según sea el sistema de ficheros y según las operaciones de ficheros que se hayan realizado desde el borrado.

Por tanto, normalmente las imágenes de disco que hayan tenido mucho trajín de archivos y que no tengan mucho espacio sobrante, tendrán pocos bloques que puedan ser eliminados durante la compactación, ya que esos bloques es muy posible que contengan restos de ficheros ya eliminados y no contengan solo ceros.

Para tener éxito en la compactación, hemos de llenar todos esos bloques no usados de ceros previamente. Una forma muy sencilla de hacerlo es montar la partición y crear un fichero muy grande, que llene el sistema de ficheros, y que sólo contenga ceros. Todo el espacio que antes estaba libre lo ocupará ahora un archivo lleno de ceros que, si a continuación borramos, volveremos a tener el espacio libre, pero ahora lleno de ceros. Lo podemos hacer muy fácilmente con un dd desde /dev/zero, y el comando lo podemos ejecutar sobre la partición montada en el sistema operativo Host o desde dentro del sistema operativo Guest cuando éste está arriba y funcionando.

Por ejemplo, si lo queremos hacer desde el Host sobre una imagen vdi de tamaño fijo, lo haremos así:

$ vditool DUMP imagen_mem_usb.vdi | grep offData
Header: offBlocks=512 offData=31232

$ sudo mount -o loop,offset=$(( 31232 + 63 * 512 )) imagen_mem_usb.img /mnt/imgusb

$ cd /mnt/imgusb

$ sudo dd if=/dev/zero of=ceros
dd: writing to `zeros': No space left on device
5677697+0 records in
5677696+0 records out
2906980352 bytes (2.9 GB) copied, 749.509 s, 3.9 MB/s

$ rm ceros

y al compactar:

$ vditool SHRINK imagen_mem_usb.vdi 
vditool    Copyright (c) 2008 Sun Microsystems, Inc.

Shrinking VDI image file="imagen_mem_usb.vdi"...
progress: 0%Log created: 2008-10-05T17:59:58.749328000Z
Executable: /usr/lib/virtualbox/vditool
Arg[0]: /usr/lib/virtualbox/vditool
Arg[1]: SHRINK
Arg[2]: imagen_mem_usb.vdi
Dumping VDI image "imagen_mem_usb.vdi" mode=r/w fOpen=0 File=00000003
Header: Version=00010001 Type=2 Flags=0 Size=8000004096
Header: cbBlock=1048576 cbBlockExtra=0 cBlocks=7630 cBlocksAllocated=7630
Header: offBlocks=512 offData=31232
Header: Geometry: C/H/S=0/0/0 cbSector=512
Header: uuidCreation={764143e6-e068-4f86-bae6-51de1fd4b374}
Header: uuidModification={4ef44cb6-1d79-4fa2-8e00-d6985bdcc6e8}
Header: uuidParent={00000000-0000-0000-0000-000000000000}
Header: uuidParentModification={00000000-0000-0000-0000-000000000000}
Image:  fFlags=00000000 offStartBlocks=512 offStartData=31232
Image:  uBlockMask=000FFFFF uShiftIndex2Offset=20 uShiftOffset2Index=20 offStartBlockData=0
...........10%..........20%..........30%..........40%..........50%
..........60%..........70%..........80%..........90%..........100%
Dumping VDI image "imagen_mem_usb.vdi" mode=r/w fOpen=0 File=00000003
Header: Version=00010001 Type=2 Flags=0 Size=8000004096
Header: cbBlock=1048576 cbBlockExtra=0 cBlocks=7630 cBlocksAllocated=4918
Header: offBlocks=512 offData=31232
Header: Geometry: C/H/S=0/0/0 cbSector=512
Header: uuidCreation={764143e6-e068-4f86-bae6-51de1fd4b374}
Header: uuidModification={4ef44cb6-1d79-4fa2-8e00-d6985bdcc6e8}
Header: uuidParent={00000000-0000-0000-0000-000000000000}
Header: uuidParentModification={00000000-0000-0000-0000-000000000000}
Image:  fFlags=00000000 offStartBlocks=512 offStartData=31232
Image:  uBlockMask=000FFFFF uShiftIndex2Offset=20 uShiftOffset2Index=20 offStartBlockData=0

The operation completed successfully!

esta vez sí que estaremos ganado todo el espacio posible, todo el que de verdad no se esté usando en el sistema de ficheros:

-rwxr-xr-x  1 root    root    8000004096 2008-09-26 23:41 imagen_mem_usb.img*
-rw-------  1 vicente vicente 5156928000 2008-10-05 20:13 imagen_mem_usb.vdi*

Ese mismo dd lo podríamos realizar dentro de un Linux virtualizado o incluso dentro de un Windows virtualizado con el dd del Cygwin, por ejemplo.

Sin embargo, este sencillo sistema, que tiene la ventaja de que no necesitamos ninguna herramienta externa, tiene la desventaja de que por unos minutos tendremos el sistema de ficheros casi lleno, y por unos segundos, completamente lleno, con los problemas que esto nos podría causar en un sistema en producción real. Es por eso que el Tutorial: All about VDIs, en la sección How can I reduce the size of a dynamic VDI on disk?, nos propone usar herramientas externas para llenar de ceros los bloques no usados por ningún fichero actual. Para Windows nos propone el SDelete, que usaremos así para referirnos a las particiones C y D:

sdelete -c C
sdelete -c D 

y para Linux nos propone el zerofree, que se ha de usar con particiones ext2 y ext3 montadas sólo de lectura:

mount -n -o remount,ro -t ext2 /dev/sda1 /
zerofree /dev/sda1 

vditool vs VBoxManage

La herramienta vditool, de la que venimos haciendo un uso extensivo, no está incluida en la versión cerrada de VirtualBox, aunque sigue estando incluso en las las versiones más recientes de VirtualBox-OSE. Entendemos, por tanto, que el fabricante tal vez quiere que dejemos de usar esta herramienta en futuras versiones. ¿Qué alternativas tenemos?

Su reemplazo lo podemos encontrar en las diferentes opciones del comando VBoxManage:

VBoxManage registerimage    disk|dvd|floppy <filename>
                            [-type normal|immutable|writethrough] (disk only)

VBoxManage unregisterimage  disk|dvd|floppy <uuid>|<filename>

VBoxManage showvdiinfo      <uuid>|<filename>

VBoxManage createvdi        -filename <filename>
                            -size <megabytes>
                            [-static]
                            [-comment <comment>]
                            [-register]
                            [-type normal|writethrough] (default: normal)

VBoxManage modifyvdi        <uuid>|<filename>
                            compact

VBoxManage clonevdi         <uuid>|<filename> <outputfile>

VBoxManage convertdd        [-static] <filename> <outputfile>
VBoxManage convertdd        [-static] stdin <outputfile> <bytes>
VBoxManage internalcommands converttoraw [-format <fileformat>] <filename> <outputfile>

Así, el “vditool DD” lo podemos sustituir con un “VBoxManage convertdd“; el “vditool COPYDD” lo podemos hacer con un “VBoxManage internalcommands converttoraw“; y el “vditool SHRINK” lo reemplazaremos con un “VBoxManage modifyvdi /path/imagen.vdi compact“:

$ VBoxManage modifyvdi /path/imagen_mem_usb.vdi compact
VirtualBox Command Line Management Interface Version 2.0.2
(C) 2005-2008 Sun Microsystems, Inc.
All rights reserved.

Shrinking '/path/imagen_mem_usb.vdi': 0%...........10%..........20%..........30%..........40%..........50%
..........60%..........70%..........80%..........90%..........100%

Únicamente tenemos que tener en cuenta que si no tenemos la imagen registrada (“VBoxManage registerimage disk” o con el interfaz gráfico) nos aparecerán errores como estos, aunque la tarea se hará igualmente:

$ VBoxManage modifyvdi /path/imagen_mem_usb.vdi compact
VirtualBox Command Line Management Interface Version 2.0.2
(C) 2005-2008 Sun Microsystems, Inc.
All rights reserved.

[!] FAILED calling virtualBox->FindVirtualDiskImage(filepath, vdi.asOutParam()) at line 3320!
[!] Primary RC  = NS_ERROR_INVALID_ARG (0x80070057) - Invalid argument value
[!] Full error info present: true , basic error info present: true 
[!] Result Code = NS_ERROR_INVALID_ARG (0x80070057) - Invalid argument value
[!] Text        = Could not find a registered VDI hard disk with the file path '/path/imagen_mem_usb.vdi'
[!] Component   = VirtualBox, Interface: IVirtualBox, {557a07bc-e6ae-4520-a361-4a8493199137}
[!] Callee      = IVirtualBox, {557a07bc-e6ae-4520-a361-4a8493199137}
Shrinking '/path/imagen_mem_usb.vdi': 0%...........10%..........20%..........30%..........40%..........50%
..........60%..........70%..........80%..........90%..........100%

Mucho peor lo tenemos con el “vditool DUMP“, necesario para montar las imágenes de tamaño fijo, puesto que no tiene un reemplazo en el “VBoxManage showvdiinfo“:

$ VBoxManage showvdiinfo /path/imagen_mem_usb.vdi 
VirtualBox Command Line Management Interface Version 2.0.2
(C) 2005-2008 Sun Microsystems, Inc.
All rights reserved.

UUID:                 53bea69e-c21b-4273-be9e-df3fa8f1ffd7
Registered:           no
Accessible:           yes
Size:                 7629 MBytes
Current size on disk: 4968 MBytes
Type:                 standard
Storage type:         Virtual Disk Image (VDI)
Path:                 /path/imagen_mem_usb.vdi

Sin embargo, hay una cosa muy útil que VBoxManage sí aporta sobre el vditool, y es que desde junio (es decir, en las versiones 2.0.X), el VBoxManage permite crear imágenes vdi de tamaño dinámico directamente desde la imagen creada con dd, dando la opción de crear una imagen de tamaño estático con la opción -static sólo si lo especificamos:

VBoxManage convertdd        [-static] <filename> <outputfile>

Probémoslo convirtiendo una imagen de dd en la que hemos escrito ceros en todo el espacio vacío a un vdi de tamaño dinámico directamente:

$ VBoxManage convertdd imagen_mem_usb_ceros.img imagen_mem_usb_vbm.vdi
VirtualBox Command Line Management Interface Version 2.0.2
(C) 2005-2008 Sun Microsystems, Inc.
All rights reserved.

Converting VDI: from DD image file="imagen_mem_usb_ceros.img" to file="imagen_mem_usb_vbm.vdi"...
Creating dynamic image with size 8000004096 bytes (7630MB)...

Vemos que el resultado es exactamente el mismo que anteriormente tras los pasos “vditool DD” y “vditool SHRINK“:

-rwxr-xr-x  1 root    root    8000004096 2008-10-05 18:17 imagen_mem_usb_ceros.img*
-rw-------  1 vicente vicente 5156928000 2008-10-05 20:36 imagen_mem_usb.vdi*
-rw-------  1 vicente vicente 5156928000 2008-10-05 20:51 imagen_mem_usb_vbm.vdi*

:wq

Entradas relacionadas

2 Comentarios a “Compactar imágenes de disco de VirtualBox: vditool vs VBoxManage”

Trackbacks y pingbacks:

Tema LHYLE09, creado por Vicente Navarro