Jugando con device-mapper y loop
Hace unos días tuve que mover un archivo grande de un sistema Linux a otro. Y cuando digo grande, me refiero a alrededor de 10 GB, por lo que transmitirlo por la red no es una opción muy atractiva, sobre todo cuando tengo un disco USB externo con espacio libre de sobra. Pero, ah, este disco está formateado con FAT32 por cuestiones de interoperatibilidad con otros sistemas operativos, y en este sistema el tamaño máximo de archivo es de solamente 4 GB. ¿Qué hice? Lo resolví con la misma técnica que hace 15 años usando disquetes y MS-DOS: un archivo RAR multivolumen con nivel de compresión cero, se crean 5 archivos de 2 GB cada uno y se copian sin problemas. Pero estamos en el siglo XXI y tenemos Linux, tiene que haber una forma más elegante de hacer esto.
Y la hay, por supuesto. Mi propuesta es crear unos cuantos archivos en el sistema FAT32 que juntos sumen el espacio necesario, y luego construir sobre ellos un dispositivo de bloques lo bastante granda para contener el archivo en cuestión. Para esto utilizaré dos drivers de Linux: loop y device-mapper.
loop
La función del driver loop es presentar los contenidos de un archivo ordinario como un dispositivo de bloques. Su uso más habitual es para montar imágenes de disco (notablemente archivos .iso). Tanto es así que el comando mount puede utilizar loop directamente:
# mount -o loop cdrom.iso /mntEste comando conecta primero el archivo cdrom.iso a un dispositivo loop libre, por ejemplo /dev/loop0, y luego utiliza este último para el comando de montaje.
En caso de necesidad se pueden gestionar los dispositivos loop de forma manual con el comando losetup.
device-mapper
Este driver crea un dispositivo de bloques utilizando como almacenamiento otros dispositivos de bloques o rangos dentro de ellos. Está detrás de otras tecnologías más conocidas como LVM (Logical Volume Management), que sirve de abstracción entre las particiones de disco y los sistemas de archivo, o RAIDs software, que configuran varios discos físicos como un único disco lógico.
Device-mapper es un driver de bastante bajo nivel, diseñado para ser utilizado como base para otras tecnologías, y no está pensado para ser utilizado directamente por el usuario, aunque no hay nada que lo impida. De hecho esto es lo que haré a continuación.
La solución
El primer paso es crear unos cuantos archivos en un directorio del disco externo. Hagamos, por ejemplo, 32 archivos de 512 MB cada uno, para hacer un total de 16 GB. Es mejor no hacer archivos muy grandes, porque FAT32 no es particularmente eficiente en esos casos. El siguiente comando tardará un rato:
$ mkdir /media/disk/sf $ for x in f{0..31} do echo Creando $x... dd if=/dev/zero of=/media/disk/sf/$x bs=1M count=512 done
Ahora hay que conectar cada uno de esos archivos a un dispositivo loop. El problema aquí es que Linux por defecto solamente crea 8 dispositivos loop, pero necesitamos 32, además de alguno extra por si acaso se necesitan para otras cosas:
$ ls /dev/loop* /dev/loop0 /dev/loop2 /dev/loop4 /dev/loop6 /dev/loop1 /dev/loop3 /dev/loop5 /dev/loop7
No hay problema. Consultando la documentación de Linux (drivers/block/loop.c):
Se crean 8 dispositivos loop al cargar el módulo,
y el ususario puede añadir dispositivos adicionales creando
nodos dev él mismo, de esta manera el kernel instancia los
dispositivos en cuanto se necesitan
Es decir, basta con crear los nodos en /dev, con número mayor 7, y el kernel se encarga de todo lo demás (hay que hacerse root, claro):
$ sudo -i # for x in {8..39} do mknod /dev/loop$x b 7 $x done
Ahora vamos a conectar los archivos creados antes con estos dispositivos:
# for x in f{0..31} do echo loop $x losetup --find --show $x done
La opción –find indica que utilice el primer dispositivo libre que encuentre, y –show escribe en la consola el nombre de ese dispositivo, para saber cuál conecta a cada archivo.
Una vez hecho eso podemos confirmar que ha ido bien con:
# losetup -aYa solo queda decirle al device-mapper que cree un nuevo dispositivo con todos estos concatenados. Para eso se necesita un fichero con la tabla de dispositivos y rangos de bloques que se quieren utilizar. Vamos a modificar el bucle de antes para que al mismo tiempo que configura los loops cree el fichero (puedes desconectar los loops con losetup -d /dev/loop*):
# POS=0 # SIZE=$((2048 * 512)) # rm -f table # for x in f{0..31} do LO=`losetup -f --show $x` echo "$POS $SIZE linear $LO 0" >> table POS=$((POS + SIZE)) done
Esto requiere algunas explicaciones. SIZE es el tamaño de cada uno de los ficheros, en bloques de 512 bytes (1 MB es igual a 2048 bloques de 512 bytes, y cada fichero es de 512 MB). Cada línea del fichero table contiene un rango de bloques, la palabra linear, el nombre del dispositivo al que corresponde, y un offset, también en bloques, dentro de ese dispositivo. En mi caso el resultado es:
0 1048576 linear /dev/loop0 0 1048576 1048576 linear /dev/loop1 0 2097152 1048576 linear /dev/loop2 0 3145728 1048576 linear /dev/loop3 0 4194304 1048576 linear /dev/loop4 0 5242880 1048576 linear /dev/loop5 0 6291456 1048576 linear /dev/loop6 0 7340032 1048576 linear /dev/loop7 0 8388608 1048576 linear /dev/loop8 0 9437184 1048576 linear /dev/loop9 0 10485760 1048576 linear /dev/loop10 0 11534336 1048576 linear /dev/loop11 0 12582912 1048576 linear /dev/loop12 0 13631488 1048576 linear /dev/loop13 0 14680064 1048576 linear /dev/loop14 0 15728640 1048576 linear /dev/loop15 0 16777216 1048576 linear /dev/loop16 0 17825792 1048576 linear /dev/loop17 0 18874368 1048576 linear /dev/loop18 0 19922944 1048576 linear /dev/loop19 0 20971520 1048576 linear /dev/loop20 0 22020096 1048576 linear /dev/loop21 0 23068672 1048576 linear /dev/loop22 0 24117248 1048576 linear /dev/loop23 0 25165824 1048576 linear /dev/loop24 0 26214400 1048576 linear /dev/loop25 0 27262976 1048576 linear /dev/loop26 0 28311552 1048576 linear /dev/loop27 0 29360128 1048576 linear /dev/loop28 0 30408704 1048576 linear /dev/loop29 0 31457280 1048576 linear /dev/loop30 0 32505856 1048576 linear /dev/loop31 0
Ya estamos listos para crear el dispositivo del device-mapper:
# dmsetup create sf < tableEste comando crea el dispositivo de bloques /dev/mapper/sf, de 16 GB de tamaño, como puede comprobarse con el siguiente comando:
# blockdev --getsize64 /dev/mapper/sf 17179869184
Claro que antes de montarlo hay que crear el sistema de archivos. Usaremos ext2:
# mkfs.ext2 /dev/mapper/sf¡Y ya podemos montarlo!:
# mount /dev/mapper/sf /mnt # df -h /mnt /dev/mapper/sf 16G 44M 15G 1% /mnt
Ahora copiamos el fichero de 10 GB, desmontamos el device-mapper, desmontamos el disco USB, y podemos llevarlo al otro sistema Linux, donde haremos la operación contraria.
Lo sorprendente es que la pérdida de rendimiento es mínima, comparada con escribir en el disco FAT32 directamente, teniendo en cuenta que es un sistema ext2 sobre un device-mapper sobre múltiples loop sobre archivos en FAT32 sobre un disco USB.
Artículos relacionados:
- Utilidad para leer particiones Ext2/3/4 En los dos últimos posts describí el formato del sistema...
- Analizando Ext2, Ext3 y Ext4 Resulta paradójico que el sistema de archivos más utilizado de...
- Colorear la salida del compilador con sed ¿Alguna vez te has perdido intentando distinguir los errores entre...
- Extensiones de Ext2, Ext3 y Ext4 En el post anterior describía con más o menos detalle...
- Ver imágenes con LESS El comando less de GNU es mucho más que un...
RSS
Deja un comentario