Si può fare, in teoria. Ma è molto brutto e in sostanza comporta la costruzione del nostro archivio a mano.
Quello che stiamo affrontando
Il tar
formato funziona su blocchi da 512 byte . Questa dimensione è fissa e deve corrispondere alla dimensione tradizionale del settore del disco. Quando si memorizza un file in un archivio, il primo blocco da 512 byte è un'intestazione che contiene metadati del file (nome, dimensione, tipo, ecc.), Mentre i blocchi seguenti contengono il contenuto del file. Quindi i nostri dati archiviati saranno disallineati di 512 byte.
La dimensione del blocco ("--sectorize") di btrfs è in genere 4096 byte . In teoria possiamo scegliere questo, ma in pratica sembra che debba corrispondere alle dimensioni della pagina della nostra CPU. Quindi non possiamo ridurre i blocchi di btrfs.
Il tar
programma ha un concetto di una dimensione "record" più grande, definita come un multiplo della dimensione del blocco, che sembra quasi utile. Si scopre che questo ha lo scopo di specificare la dimensione del settore di una determinata unità nastro, in modo da tar
evitare la scrittura di record di nastro parziali. Tuttavia, i dati sono ancora costruiti e raggruppati in unità di 512 byte, quindi non possiamo usarli per far crescere tar
i blocchi come speravi.
Un ultimo punto di dati da sapere è che tar
's indicatore di fine-archivio è a due consecutivi blocchi tutti-zeri, tranne quando i blocchi sono i dati dei file all'interno. Quindi, qualsiasi tipo di ingenuo blocco di imbottitura probabilmente non verrà accettato.
L'hack
Quello che possiamo fare è inserire i file di riempimento. All'inizio del nostro archivio, prima di aggiungere il file che vogliamo deduplicare (chiamarlo dup
), aggiungiamo un file pad
, dimensionato in modo che
pad's header + pad's data + dup's header = 4096 bytes.
In questo modo, dup
i dati iniziano a un limite di blocco e possono essere deduplicati.
Quindi, per ogni file successivo, dobbiamo anche tenere traccia delle dimensioni del file precedente per calcolare il corretto riempimento. Dobbiamo anche prevedere se sarà necessaria una sorta di estensione dell'intestazione: ad esempio, l' intestazione tar di base ha spazio solo per 100 byte di percorso del file, quindi i percorsi più lunghi vengono codificati utilizzando quello che è strutturalmente un file con nome speciale i cui dati sono il percorso completo. In generale c'è molta potenziale complessità nel prevedere la dimensione dell'intestazione - il tar
formato del file ha molta rotta da più implementazioni storiche.
Una piccola sfumatura argentata è che tutti i file di riempimento possono condividere lo stesso nome, quindi quando apriremo la memoria finiremo con un solo file extra di dimensioni inferiori a 4096 byte.
Il modo più pulito per creare in modo affidabile un archivio come questo è probabilmente quello di modificare il tar
programma GNU . Ma se vuoi essere veloce e sporco a spese della CPU e del tempo di I / O, puoi, per ogni file, fare qualcosa del tipo:
#!/bin/bash
# Proof of concept and probably buggy.
# If I ever find this script in a production environment,
# I don't know whether I'll laugh or cry.
my_file="$2"
my_archive="$1"
file_size="$(wc -c <"$my_file")"
arch_size="$(tar cb 1 "$my_file" | wc -c)" # "b 1": Remember that record size I mentioned? Set it to equal the block size so we can measure usefully.
end_marker_size=1024 # End-of-archive marker: 2 blocks' worth of 0 bytes
hdr_size="$(( (arch_size - file_size - end_marker_size) % 4096 ))"
pad_size="$(( (4096 - 512 - hdr_size) % 4096 ))"
(( pad_size < 512 )) && pad_size="$(( pad_size + 4096 ))"
# Assume the pre-existing archive is already a multiple of 4096 bytes long
# (not including the end-of-archive marker), and add extra padding to the end
# so that it stays that way.
file_blocks_size="$(( ((file_size+511) / 512) * 512 ))"
end_pad_size="$(( 4096 - 512 - (file_blocks_size % 4096) ))"
(( end_pad_size < 512 )) && end_pad_size="$(( end_pad_size + 4096 ))"
head -c $pad_size /dev/zero > _PADDING_
tar rf "$my_archive" _PADDING_ "$my_file"
head -c $end_pad_size /dev/zero > _PADDING_
tar rf "$my_archive" _PADDING_