By

Qué es un sparse file

Hoy me he encontrado con el término sparse file y realmente no lo conocía, aunque puede que os pase como a mi, que después de que os diga qué es, muchos de vosotros ya sabíais de su existencia e incluso es muy probable que hayáis trabajado con este tipo de ficheros. Pero…

¿Qué es un sparse file?

Un sparse file es un tipo de fichero el cual se crea con un tamaño específico pero no se reserva dicho espacio en el sistema de ficheros inicialmente. Su tamaño irá creciendo a medida que vaya siendo necesario. Por ejemplo, podríamos crear un fichero cuyo tamaño máximo sea 2G pero que inicialmente en nuestro sistema de ficheros ocupe 0 bytes.

Es muy probable que ya es sea más familiar este tipo de ficheros, sobre todo aquellos que hayáis trabajado con virtualización, por ejemplo con KVM utilizando virt-manager, cuando al crear un volumen de almacenamiento para una máquina virtual ofrece la opción de alojar la totalidad del volumen en el pool de almacenamiento, o que el tamaño del volumen vaya creciendo dinámicamente.

Para temas de virtualización no es aconsejable utilizar este tipo de ficheros porque pueden generar problemas de rendimiento e integridad de los datos. De todas formas, son bastante útiles para realizar pruebas.

¿Qué ventajas me aporta?

Como ventajas puntualizaría la rapidez con la que son creados, que como he comentado anteriormente, para realizar pruebas son perfectos, ya que inicialmente “no ocupan” espacio en el sistema.

Además utilizar un sparse file nos permitirá crear un fichero de gran tamaño aunque en ese momento no dispongamos de tal espacio en nuestro sistema de ficheros.

Pero no es oro todo lo que reluce…

Este tipo de fichero es soportado en la mayoría de los sistemas de ficheros modernos en Unix, incluso en NTFS; pero al parecer no es posible, y lo he comprobado, en HFS+ de Apple.

A parte de los problemas de rendimiento e integridad con los datos comentados anteriormente, es posible que obtengamos resultados inesperados cuando tratemos de copiar este tipo de ficheros, pues si la utilidad que hemos usado para copiarlo no soporta este tipo de ficheros, será copiado con el tamaño completo. Afortunadamente cp, por defecto, y rsync con la opción -S / –sparse, soportan este tipo de ficheros.

¿Y cómo creo un sparse file?

La creación de un sparse file es muy sencillo, es posible hacerlo con el comando dd que muchos ya conoceréis al haber creado ficheros llenos de ceros de la siguiente forma:

$ dd if=/dev/zero of=file.img bs=1M count=1G

Esta instrucción creará un fichero lleno de ceros ocupando 1G de espacio en vuestro sistema de ficheros.

Pero para crear un sparse file será necesario ejecutar la siguiente instrucción, similar a la anterior:

$ dd if=/dev/zero of=file.img bs=1M count=0 seek=1024
0+0 registros leídos
0+0 registros escritos
0 bytes (0 B) copiados, 1,802e-05 s, 0,0 kB/s

El truco se encuentra en el operador seek. En este caso tendremos un fichero que inicialmente ocupa 0 bytes pero con un tamaño de 1G en potencia.

$ du -hs file.img 
0   file.img

Sin embargo, podemos ver su tamaño real con la opción –apparent-size de la siguiente forma:

$ du -h --apparent-size file.img
1,0G    file.img

¿Pero cómo compruebo que su tamaño crece dinámicamente?

Es muy sencillo, crearemos ficheros dentro de este y veremos cómo aumenta de tamaño dinámicamente hasta un total de 1G. Para ello seguiremos los siguiente pasos:

  1. Crearemos un sistema de ficheros en file.img, lo cual nos va a permitir alojar ficheros dentro de él convirtiéndolo en un volumen de almacenamiento:

    $ mkfs.ext4 -q file.img  
    file.img no es un dispositivo especial de bloques.
    ¿Continuar de todas formas? (s,n) s
    
  2. Montamos el fichero como si de un dispositivo de almacenamiento se tratase, utilizando el comando mount, de la siguiente forma:

    $ mkdir mnt
    $ sudo mount -t auto file.img mnt
    

    Si listamos los puntos de montaje actuales en nuestro sistema, deberíamos ver el fichero file.img montado en el directorio local mnt:

    $ mount |grep file.img
    /tmp/file.img on /tmp/mnt type ext4 (rw)
    
  3. Creamos varios ficheros temporales dentro del punto de montaje:

    $ for i in {1..10};do mktemp --tmpdir=mnt; done
    

    Nota: aprovecho para enseñar el comando mktemp crea un fichero temporal del tipo tmp.XXXXXXXX en el directorio actual. Con la opción –tmpdir es posible indicar el directorio destino donde se creará el fichero temporal.

  4. Comprobamos que el tamaño del fichero ha crecido con respecto al estado anterior:

    $ du -h file.img 
    49M    file.img
    

    Y obviamente el tamaño total sigue siendo 1G:

    $ du -h --apparent-size file.img 
    1,0G    file.img
    

Dónde está el límite

Lo único que nos falta comprobar es dónde está el limite. En el ejemplo utilizado es 1G, así que intentaremos crear un fichero de un tamaño mayor que el total del sparse file.

Teniendo montado el fichero en el directorio mnt, crearemos un fichero de 2G dentro de este, ejecutando lo siguiente:

$ dd if=/dev/zero of=file bs=1M count=2048
dd: escribiendo «file»: No queda espacio en el dispositivo
924+0 registros leídos
923+0 registros escritos
968290304 bytes (968 MB) copiados, 4,71871 s, 205 MB/s

Como podemos observar, nos ha avisado de que no queda espacio libre en el dispositivo y si comprobamos el tamaño actual del fichero obtendremos lo siguiente:

$ du -h file.img 
973M    file.img

Nota: Hay un resto de espacio de 27M que es posible que sea debido a la reserva del 5% de espacio para el usuario root. Siempre hay una reserva de espacio para el usuario root, por defecto del 5%, de forma que este pueda acceder al dispositivo cuando haya alcanzado su límite de espacio. Si eliminamos dicha reserva, para nada recomendable en producción pero en este caso estamos probando en un fichero local, podremos comprobar que si volvemos a crear el fichero de 2G dentro del sparse file, el espacio llega a ocuparse completamente:

$ tune2fs -m 0 file.img 
tune2fs 1.41.14 (22-Dec-2010)
Setting reserved blocks percentage to 0% (0 blocks)

$ dd if=/dev/zero of=file bs=1M count=2048
dd: escribiendo «file»: No queda espacio en el dispositivo
965+0 registros leídos
964+0 registros escritos
1011208192 bytes (1,0 GB) copiados, 3,27181 s, 309 MB/s

$ du -h file.img  
1,0G    file.img

Actualización: ¿Y si queremos aumentar el límite?

Ayer se me debió pasar esta cuestión mientras me centraba en escribir, pero sí, es posible aumentar el tamaño del sparse file, ¿pero cómo?

Pues muy sencillo, solo tenemos que volver a utilizar el comando dd indicando un tamaño mayor en el parámetro seek, como indico a continuación:

$ dd if=/dev/zero of=file.img bs=1M count=0 seek=2048
0+0 registros leídos
0+0 registros escritos
0 bytes (0 B) copiados, 1,6064e-05 s, 0,0 kB/s

Si comprobamos ahora el tamaño del fichero, podremos observar que su tamaño total a aumentado en 1G:

$ du -h --apparent-size file.img 
2,0G    file.img

Pero aun no hemos terminado, sólo nos falta redimensionar el sistema de ficheros incluido en él, y esto lo haremos haciendo uso del comando resize2fs, pero primero realizaremos un chequeo del sistema de ficheros:

e2fsck -f file.img 
e2fsck 1.41.14 (22-Dec-2010)
Paso 1: Verificando nodos-i, bloques y tamaños
Paso 2: Verificando la estructura de directorios
Paso 3: Revisando la conectividad de directorios
Paso 4: Revisando las cuentas de referencia
Paso 5: Revisando el resumen de información de grupos
file.img: 22/65536 ficheros (0.0% no contiguos), 259513/262144 bloques

$ resize2fs file.img 
resize2fs 1.41.14 (22-Dec-2010)
Resizing the filesystem on file.img to 524288 (4k) blocks.
El sistema de ficheros en file.img tiene ahora 524288 bloques.

Por último, si comprobamos el total de espacio disponible dentro del sparse file podremos observar que a aumentado a 2G:

$ df -h mnt 
S.ficheros     Tamaño Usado  Disp Uso% Montado en
/dev/loop0       2,0G  998M  998M  51% /tmp/mnt

Como decía el maestro Juan Tamariz:

Chianananaaaaaaaa… La magia no tiene trucos, por la razón de que todos sabemos que sí que los tiene.

Espero que os haya parecido interesante! No dudéis en comentar!

Un saludo, Manu.