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 tar
lo más absurdo que parecía.
Verás, tar
es 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 ustar
formato del encabezado ... tal vez . Al menos, emula lo que GNU tar
parece pensar que es el ustar
formato 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 \0
nulos, así que me convierto em
en \n
lí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 shitar
es el propósito, tar
ya 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 dd
saliendo de la cabeza de un tar
archivo 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 ustar
archivo, 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 tar
es 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, shitar
funciona 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 mount
capaz 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 stdin
compresor 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 -p
argumento en línea para mksquash
. Puede obtener un archivo que -pf
contenga 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 tar
programa 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 squashfs
sistema de archivos no era compatible, no te creería.