Jeśli prędkość jest ważna i kompresja nie jest potrzebna, można zahaczyć syscall obwolut używanych przez tar
użyciem LD_PRELOAD
, aby zmienić tar
go obliczyć dla nas. Reimplementując kilka z tych funkcji w celu zaspokojenia naszych potrzeb (obliczając rozmiar potencjalnej wyjściowej danych smoły), jesteśmy w stanie wyeliminować wiele z nich read
i write
jest to wykonywane przy normalnym działaniu tar
. To sprawia, że jest tar
znacznie szybszy, ponieważ nie musi kontekstowo przełączać się w jądro tam iz powrotem tak blisko, a tylko stat
żądany plik / folder (pliki) wejściowy musi zostać odczytany z dysku zamiast z rzeczywistych danych pliku.
Poniższy kod zawiera implementacje close
, read
oraz write
funkcji POSIX. Makro OUT_FD
kontroluje, którego deskryptora pliku będziemy tar
używać jako pliku wyjściowego. Obecnie jest ustawiony na standardowe wyjście.
read
został zmieniony tak, aby zwracał wartość sukcesu count
bajtów zamiast wypełnienia bufora danymi, biorąc pod uwagę, że rzeczywiste dane nie zostały odczytane, buf nie zawierałby prawidłowych danych do przekazania na kompresję, a zatem gdyby zastosowano kompresję, obliczilibyśmy niepoprawne rozmiar.
write
został zmieniony, aby sumować count
bajty wejściowe do zmiennej globalnej total
i zwracać wartość sukcesu count
bajtów tylko wtedy, gdy deskryptor pliku jest zgodny OUT_FD
, w przeciwnym razie wywołuje pierwotne opakowanie pozyskane przez, dlsym
aby wykonać wywołanie systemowe o tej samej nazwie.
close
nadal wykonuje wszystkie oryginalne funkcje, ale jeśli deskryptor pliku pasuje do OUT_FD, wie, że tar
próbowano zapisać plik tar, więc total
numer jest ostateczny i wypisuje go na standardowe wyjście.
#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 porównujący rozwiązanie, w którym dostęp do dysku odczytu i wszystkie wywołania systemowe normalnej operacji tar wykonywane są przeciwko LD_PRELOAD
rozwiązaniu.
$ 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
Powyższy kod, podstawowy skrypt do budowania powyższego jako biblioteka współdzielona oraz skrypt LD_PRELOAD
wykorzystujący go w „ technice” znajduje się w repozytorium:
https://github.com/G4Vi/tarsize
Kilka informacji na temat korzystania z LD_PRELOAD: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/
--totals
opcją. Tak czy inaczej, jeśli zapełnisz dysk, możesz po prostu usunąć archiwum, imho. Aby sprawdzić wszystkie dostępne opcje, możesz przejśćtar --help
.