Se la velocità è importante e la compressione non è necessaria, è possibile agganciare i wrapper syscall utilizzati tar
utilizzando LD_PRELOAD
, per modificare tar
per calcolarlo per noi. Reimplementando alcune di queste funzioni per soddisfare le nostre esigenze (calcolando la dimensione dei potenziali dati di catrame di output), siamo in grado di eliminare molte read
e write
che vengono eseguite nel normale funzionamento di tar
. Questo fatar
molto più veloce in quanto non ha bisogno di passare da un contesto all'altro nel kernel in qualsiasi punto vicino e solo il stat
file / le cartelle di input richiesto deve essere letto dal disco invece dei dati del file effettivo.
Il codice sotto include implementazioni dei close
, read
e write
funzioni POSIX. La macro OUT_FD
controlla quale descrittore di file ci aspettiamotar
di utilizzare come file di output. Attualmente è impostato su stdout.
read
è stato modificato per restituire semplicemente il valore di successo dei count
byte invece di riempire buf con i dati, dato che i dati effettivi non sono stati letti buf non conterrebbero dati validi per il passaggio alla compressione, quindi se si utilizzava la compressione calcoleremmo un errore dimensione.
write
è stato modificato per sommare i count
byte di input nella variabile globale total
e restituire il valore di successo dei count
byte solo se il descrittore di file corrisponde OUT_FD
, altrimenti chiama il wrapper originale acquisito tramitedlsym
per eseguire la syscall con lo stesso nome.
close
preforma ancora tutte le sue funzionalità originali, ma se il descrittore di file corrisponde a OUT_FD, sa che tar
si tenta di scrivere un file tar, quindi il total
numero è definitivo e lo stampa su stdout.
#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <errno.h>
#include <dlfcn.h>
#include <string.h>
#define OUT_FD 1
uint64_t total = 0;
ssize_t (*original_write)(int, const void *, size_t) = NULL;
int (*original_close)(int) = NULL;
void print_total(void)
{
printf("%" PRIu64 "\n", total);
}
int close(int fd)
{
if(! original_close)
{
original_close = dlsym(RTLD_NEXT, "close");
}
if(fd == OUT_FD)
{
print_total();
}
return original_close(fd);
}
ssize_t read(int fd, void *buf, size_t count)
{
return count;
}
ssize_t write(int fd, const void *buf, size_t count)
{
if(!original_write)
{
original_write = dlsym(RTLD_NEXT, "write");
}
if(fd == OUT_FD)
{
total += count;
return count;
}
return original_write(fd, buf, count);
}
Benchmark confrontando una soluzione in cui l'accesso al disco di lettura e tutte le syscall della normale operazione tar vengono eseguiti rispetto alla LD_PRELOAD
soluzione.
$ time tar -c /media/storage/music/Macintosh\ Plus-\ Floral\ Shoppe\ \(2011\)\ \[Flac\]/ | wc -c
332308480
real 0m0.457s
user 0m0.064s
sys 0m0.772s
tarsize$ time ./tarsize.sh -c /media/storage/music/Macintosh\ Plus-\ Floral\ Shoppe\ \(2011\)\ \[Flac\]/
332308480
real 0m0.016s
user 0m0.004s
sys 0m0.008s
Il codice sopra, uno script di compilazione di base per creare quanto sopra come libreria condivisa e uno script con la " LD_PRELOAD
tecnica" che lo utilizza viene fornito nel repository:
https://github.com/G4Vi/tarsize
Alcune informazioni sull'uso di LD_PRELOAD: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/
--totals
opzione. In ogni caso, se riempi il disco, puoi semplicemente eliminare l'archivio, imho. Per verificare tutte le opzioni disponibili è possibile passare attraversotar --help
.