¿Cómo convencer al alquitrán (etc.) para archivar el contenido del dispositivo de bloque?

13

Tengo seis volúmenes lógicos de Linux que juntos respaldan una máquina virtual. La VM está actualmente apagada, por lo que es fácil tomar imágenes consistentes de ellos.

Me gustaría agrupar las seis imágenes juntas en un archivo. Trivialmente, podría hacer algo como esto:

cp /dev/Zia/vm_lvraid_* /tmp/somedir
tar c /tmp/somedir | whatever

Pero eso, por supuesto, crea una copia extra. Me gustaría evitar la copia extra.

El enfoque obvio:

tar c /dev/Zia/vm_lvraid_* | whatever

no funciona, ya que tar reconoce los archivos de forma especial (enlaces simbólicos en este caso) y básicamente los almacena ln -sen el archivo. O, con --dereferenceo directamente señalado /dev/dm-X, los reconoce como especiales (archivos de dispositivo) y básicamente los almacena mknoden el archivo.

He buscado opciones de línea de comandos para tar para anular este comportamiento, y no pude encontrar ninguna. También probé el cpiomismo problema y tampoco pude encontrar ninguna opción para anularlo allí. También lo intenté 7z(ídem). Lo mismo con pax. Incluso lo intenté zip, lo cual se confundió.

editar: Mirando el código fuente de GNU tar y GNU cpio, parece que ninguno de ellos puede hacer esto. Al menos, no sin trucos serios (el manejo especial de los archivos del dispositivo no se puede deshabilitar). Por lo tanto, se agradecerían sugerencias de trucos serios o utilidades alternativas.

TLDR: ¿Hay algún archivador que empaquetará varias imágenes de disco juntas (tomadas de dispositivos sin formato) y transmitirá esa salida, sin hacer copias adicionales en el disco? Mi preferencia se generaría en un formato común, como POSIX o GNU tar.

derobert
fuente
Lo convencí
mikeserv

Respuestas:

11

Así que recientemente quería hacer esto con tar. Algunas investigaciones me indicaron que era más que un poco absurdo que no pudiera. Se me ocurrió esta split --filter="cat >file; tar -r ..."cosa extraña , pero, bueno, fue terriblemente lenta. Y cuanto más leía sobre tarlo más absurdo que parecía.

Verás, tares solo una lista concatenada de registros. Los archivos constituyentes no se alteran de ninguna manera, están completos dentro del archivo. Pero están bloqueados en los límites de bloqueo de 512 bytes , y antes de cada archivo hay un encabezado . Eso es. El formato del encabezado es realmente muy simple también.

Entonces, escribí el mío tar. Lo llamo ... shitar.

z() (IFS=0; printf '%.s\\0' $(printf "%.$(($1-${#2}))d"))
chk() (IFS=${IFS#??}; set -f; set -- $(     
        printf "$(fmt)" "$n" "$@" '' "$un" "$gn"               
);  IFS=; a="$*"; printf %06o "$(($(
        while printf %d+ "'${a:?}"; do a=${a#?}; done 2>/dev/null
)0))")                                                                 
fmt() { printf '%s\\'"${1:-n}" %s "${1:+$(z 99 "$n")}%07d" \
    %07o %07o %011o %011o "%-${1:-7}s" ' 0' "${1:+$(z 99)}ustar  " %s \
    "${1:+$(z 31 "$un")}%s"
}

Esa es la carne y las papas, de verdad. Escribe los encabezados y calcula el chksum, que, relativamente hablando, es la única parte difícil. Hace el ustarformato del encabezado ... tal vez . Al menos, emula lo que GNU tarparece pensar que es el ustarformato del encabezado hasta el punto de que no se queja. Y hay más, es que todavía no lo he coagulado realmente . Aquí te muestro:

for f in 1 2; do echo hey > file$f; done
{ tar -cf - file[123]; echo .; } | tr \\0 \\n | grep -b .

0:file1                      #filename - first 100 bytes
100:0000644                  #octal mode - next 8
108:0001750                  #octal uid,
116:0001750                  #gid - next 16
124:00000000004              #octal filesize - next 12
136:12401536267              #octal epoch mod time - next 12
148:012235                   #chksum - more on this
155: 0                       #file type - gnu is weird here - so is shitar
257:ustar                    #magic string - header type
265:mikeserv                 #owner
297:mikeserv                 #group - link name... others shitar doesnt do
512:hey                      #512-bytes - start of file   
1024:file2                   #512 more - start of header 2
1124:0000644
1132:0001750
1140:0001750
1148:00000000004
1160:12401536267
1172:012236
1179: 0
1281:ustar  
1289:mikeserv
1321:mikeserv
1536:hey
10240:.                     #default blocking factor 20 * 512

Eso es tar. Todo está relleno de \0nulos, así que me convierto emen \nlíneas electrónicas para facilitar la lectura. Y shitar:

#the rest, kind of, calls z(), fmt(), chk() + gets $mdata and blocks w/ dd
for n in file[123]
do d=$n; un=$USER; gn=$(id --group --name)
   set -- $(stat --printf "%a\n%u\n%g\n%s\n%Y" "$n")
   printf "$(fmt 0)" "$n" "$@" "$(chk "$@")" "$un" "$gn"
   printf "$(z $((512-298)) "$gn")"; cat "$d"  
   printf "$(x=$(($4%512));z $(($4>512?($x>0?$x:512):512-$4)))"
done |
{ dd iflag=fullblock conv=sync bs=10240 2>/dev/null; echo .; } |
tr \\0 \\n | grep -b .

SALIDA

0:file1                 #it's the same. I shortened it.
100:0000644             #but the whole first file is here
108:0001750
116:0001750
124:00000000004
136:12401536267
148:012235              #including its checksum
155: 0
257:ustar  
265:mikeserv
297:mikeserv
512:hey
1024:file2
...
1172:012236             #and file2s checksum
...
1536:hey
10240:.

Digo un poco allá porque ese no shitares el propósito, tarya lo hace maravillosamente. Solo quería mostrar cómo funciona, lo que significa que necesito tocar el chksum. Si no fuera por eso, simplemente estaría ddsaliendo de la cabeza de un tararchivo y listo. Eso puede funcionar incluso a veces, pero se vuelve complicado cuando hay varios miembros en el archivo. Aún así, el chksum es realmente fácil.

Primero, haga 7 espacios, (lo cual es una cosa extraña de gnu, creo, como dice la especificación 8, pero lo que sea, un truco es un truco) . Luego sume los valores octales de cada byte en el encabezado. Ese es tu chksum. Por lo tanto, necesita los metadatos del archivo antes de hacer el encabezado, o no tiene un chksum. Y eso es un ustararchivo, en su mayoría.

Okay. Ahora, lo que debe hacer:

cd /tmp; mkdir -p mnt     
for d in 1 2 3                                                
do  fallocate -l $((1024*1024*500)) disk$d
    lp=$(sudo losetup -f --show disk$d)
    sync
    sudo mkfs.vfat -n disk$d "$lp"
    sudo mount  "$lp" mnt
    echo disk$d file$d | sudo tee mnt/file$d
    sudo umount mnt
    sudo losetup -d "$lp"
done

Eso genera tres imágenes de disco de 500M, formatos y montajes cada uno, y escribe un archivo en cada uno.

for n in disk[123]
do d=$(sudo losetup -f --show "$n")
   un=$USER; gn=$(id --group --name)
   set -- $(stat --printf "%a\n%u\n%g\n$(lsblk -bno SIZE "$d")\n%Y" "$n")
   printf "$(fmt 0)" "$n" "$@" "$(chk "$@")" "$un" "$gn"
   printf "$(z $((512-298)) "$gn")"
   sudo cat "$d"
   sudo losetup -d "$d"
done | 
dd iflag=fullblock conv=sync bs=10240 2>/dev/null |
xz >disks.tar.xz

Nota : aparentemente los dispositivos de bloqueo siempre se bloquearán correctamente. Bastante práctico

Ese tares el contenido de los archivos del dispositivo de disco in-stream y canaliza la salida a xz.

ls -l disk*
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk1
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk2
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk3
-rw-r--r-- 1 mikeserv mikeserv    229796 Sep  3 01:05 disks.tar.xz

Ahora, el momento de la verdad...

 xz -d <./disks.tar.xz| tar -tvf -
-rw-r--r-- mikeserv/mikeserv 524288000 2014-09-03 01:01 disk1
-rw-r--r-- mikeserv/mikeserv 524288000 2014-09-03 01:01 disk2
-rw-r--r-- mikeserv/mikeserv 524288000 2014-09-03 01:01 disk3

¡Hurra! Extracción...

xz -d <./disks.tar.xz| tar -xf - --xform='s/[123]/1&/'  
ls -l disk*
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk1
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk11
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk12
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk13
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk2
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk3
-rw-r--r-- 1 mikeserv mikeserv    229796 Sep  3 01:05 disks.tar.xz

Comparación...

cmp disk1 disk11 && echo yay || echo shite
yay

Y el monte ...

sudo mount disk13 mnt
cat mnt/*
disk3 file3

Y así, en este caso, shitarfunciona bien, supongo. Prefiero no entrar en todas las cosas que no van a hacer bien. Pero, diré, al menos no hagas nuevas líneas en los nombres de archivo.

También puede hacerlo, y tal vez debería, teniendo en cuenta las alternativas que le he ofrecido squashfs. No solo obtienes el archivo único creado a partir de la transmisión, sino que también es mountcapaz y está integrado en el núcleo vfs:

De pseudo-file.example :

# Copy 10K from the device /dev/sda1 into the file input.  Ordinarily
# Mksquashfs given a device, fifo, or named socket will place that special file
# within the Squashfs filesystem, this allows input from these special
# files to be captured and placed in the Squashfs filesystem.
input f 444 root root dd if=/dev/sda1 bs=1024 count=10

# Creating a block or character device examples

# Create a character device "chr_dev" with major:minor 100:1 and
# a block device "blk_dev" with major:minor 200:200, both with root
# uid/gid and a mode of rw-rw-rw.
chr_dev c 666 root root 100 1
blk_dev b 666 0 0 200 200

También puede usar btrfs (send|receive)para transmitir un subvolumen a cualquier stdincompresor capaz que desee. Este subvolumen no necesita existir antes de que decida usarlo como contenedor de compresión, por supuesto.

Aún así, sobre squashfs...

No creo que le esté haciendo justicia. Aquí hay un ejemplo muy simple:

 cd /tmp; mkdir ./emptydir
 mksquashfs ./emptydir /tmp/tmp.sfs -p \
    'file f 644 mikeserv mikeserv echo "this is the contents of file"'                             

Parallel mksquashfs: Using 6 processors
Creating 4.0 filesystem on /tmp/tmp.sfs, block size 131072.
[==================================================================================|] 1/1 100%
Exportable Squashfs 4.0 filesystem, gzip compressed, data block size 131072
        compressed data, compressed metadata, compressed fragments,... 
###...
###AND SO ON
###...

echo '/tmp/tmp.sfs /tmp/imgmnt squashfs loop,defaults,user 0 0'|
    sudo tee -a /etc/fstab >/dev/null

mount ./tmp.sfs     
cd ./imgmnt
ls

total 1
-rw-r--r-- 1 mikeserv mikeserv 29 Aug 20 11:34 file

cat file

this is the contents of file

cd ..
umount ./imgmnt

Ese es solo el -pargumento en línea para mksquash. Puede obtener un archivo que -pfcontenga tantos como desee. El formato es simple: define el nombre / ruta de un archivo de destino en el nuevo sistema de archivos, le asigna un modo y un propietario, y luego le dice desde qué proceso ejecutar y leer stdout. Puede crear tantos como desee, y puede usar LZMA, GZIP, LZ4, XZ ... hmm, hay más ... formatos de compresión que desee. Y el resultado final es un archivo en el que usted cd.

Más sobre el formato sin embargo:

Esto, por supuesto, no es solo un archivo, es una imagen de sistema de archivos de Linux comprimida y montable. Su formato es el del núcleo de Linux: es un sistema de archivos compatible con el núcleo de vainilla. De esta manera, es tan común como el kernel Linux de vainilla. Entonces, si me dijeras que estabas ejecutando un sistema Linux de vainilla en el que el tarprograma no estaba instalado, dudaría, pero probablemente te creería. Pero si me dijeras que estabas ejecutando un sistema Linux de vainilla en el que el squashfssistema de archivos no era compatible, no te creería.

mikeserv
fuente
Mike, ¿podríamos molestarte para que crees un pequeño ejemplo autónomo para que la gente pueda experimentar con él? Parece que podrías estar haciendo al menos parte de eso arriba, pero no estoy seguro. ¿En input f 444 root root dd if=/dev/sda1 bs=1024 count=10f es la entrada del archivo? ¿Quizás sería mejor crear un dispositivo de juguete, llenarlo con datos y escribir desde él? ¿Y todo esto requiere root?
Faheem Mitha
@ FaheemMitha: sí, puedo hacer eso, pero no lo hice aquí. El enlace es a la documentación oficial, se extrae directamente de él. Sin embargo, sería mejor si hiciera un ejemplo de comando. Lo he hecho antes, es genial. De todos modos, el inputarchivo es un archivo en el squashfsarchivo, la imagen del sistema de archivos que resulta de ejecutar el comando. Cuando lo haga mksquash, puede especificar estos comandos de pseudofile para los comandos que se ejecutan y desde los cuales stdoutse captura en el momento de la compresión.
mikeserv
@FaheemMitha - oh, y no requiere root para hacer la compresión , aunque puede hacer el montaje - es una imagen del sistema de archivos que resulta. Es el mismo sistema de archivos que usan todos los discos Linux Live. De hecho, una cosa muy interesante es que puede crear una imagen propiedad de root utilizando esos pseudo archivos sin ser root, como configurar los archivos de su dispositivo y los números arbitrarios MAJ: MIN.
mikeserv
Supongo que debería ser posible crear un archivo de dispositivo, escribir en él y luego desde él sin montarlo, ¿verdad? Entonces, tal vez no requiera root, lo que obviamente sería preferible.
Faheem Mitha
Bueno, no hay btrfs involucrados aquí, así que eso no funcionará. Pero squashfs es lo suficientemente loco como para funcionar. Aunque tiene la desventaja de no ser un formato de archivo común.
derobert
4

Tu problema me dejó perplejo por algún tiempo, y creo que he encontrado una solución que funcionaría.

Creo que puedes lograr lo que quieres con 7z usando la -si{NAME}bandera.

Podrá adaptarse a su necesidad.

7z a test.7z -siSDA2.txt < /dev/sda1
7z a test.7z -siSDA2.txt < /dev/sda2

7z l test.7z 

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,8 CPUs)

Listing archive: test.7z

--
Path = test.7z
Type = 7z
Method = LZMA
Solid = -
Blocks = 2
Physical Size = 1770
Headers Size = 162

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2014-08-19 22:01:08 .....         6314          804  SDA1.txt
2014-08-19 22:01:11 .....         6314          804  SDA2.txt
------------------- ----- ------------ ------------  ------------------------
                                 12628         1608  2 files, 0 folders

EDITAR : eliminar el uso inútil del gato

Tony
fuente
Sería útil tener un pequeño ejemplo que las personas puedan probar. Por ejemplo, crear un dispositivo de bloque, escribir en él y luego escribir desde él. No requerir root sería una ventaja.
Faheem Mitha
En el ejemplo / dev / sda1 es un dispositivo de bloque. El comando cat tiene el propósito de descargar el contenido del dispositivo en stdout. Luego, 7z crea (o actualiza) el archivo y almacena los datos en el nombre de archivo especificado por el parámetro -si de stdin. El resultado dentro del archivo es el contenido de cada dispositivo de bloque. El comando "cat" necesita root para leer los datos del dispositivo.
Tony
Ese es un uso inútil de Cat , pero por lo demás se ajusta bastante bien. Curiosamente mi página de 7zmanual no menciona -si puede tomar un nombre de archivo, pero funciona. No es perfecto (la salida no se puede canalizar en alguna parte), pero definitivamente es la mejor hasta ahora que las salidas en un formato común.
derobert
@FaheemMitha que requiera root o no dependerá de la configuración de permisos en su sistema, aunque solo root puede crear nuevos dispositivos de bloque.
derobert
@derobert Eliminó el gato :)
Tony