Si la velocidad es importante y no se necesita compresión, puede enganchar los envoltorios de syscall que se tarusan LD_PRELOAD, para cambiarlos tary calcularlos por nosotros. Al reimplementar algunas de estas funciones para satisfacer nuestras necesidades (calcular el tamaño de los datos de alquitrán de salida potencial), podemos eliminar muchas ready writeeso se realiza en la operación normal de tar. Esto es tarmucho más rápido, ya que no es necesario cambiar de contexto de un lado a otro en el kernel ni mucho menos, y solo el stat/ los archivo / carpeta (s) de entrada solicitados debe leerse desde el disco en lugar de los datos del archivo real.
El código siguiente incluye implementaciones de las close, ready writefunciones POSIX. La macro OUT_FDcontrola qué descriptor de archivo esperamos tarusar como archivo de salida. Actualmente está configurado en stdout.
readse cambió para devolver el valor de éxito de countbytes en lugar de llenar buf con los datos, dado que los datos reales no se leyeron buf no contendrían datos válidos para pasar a compresión, y por lo tanto, si se usara compresión, calcularíamos tamaño.
writese modificó para sumar los countbytes de entrada en la variable global totaly devolver el valor de éxito de los countbytes solo si el descriptor de archivo coincide OUT_FD, de lo contrario, llama al reiniciador original adquirido a través dlsymde realizar la llamada al sistema del mismo nombre.
closetodavía realiza todas sus funciones originales, pero si el descriptor de archivo coincide con OUT_FD, sabe que tarse ha intentado escribir un archivo tar, por lo que el totalnúmero es final y lo imprime en 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);
}
Prueba comparativa que compara una solución en la que el acceso al disco de lectura y todas las llamadas al sistema de la operación normal de tar se realizan contra la LD_PRELOADsolución.
$ 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
El código anterior, un script de compilación básico para compilar lo anterior como una biblioteca compartida, y un script con la " LD_PRELOADtécnica" que lo usa se proporciona en el repositorio:
https://github.com/G4Vi/tarsize
Alguna información sobre el uso de LD_PRELOAD: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/
--totalsopción. De cualquier manera, si llena el disco, simplemente puede eliminar el archivo, en mi humilde opinión. Para verificar todas las opciones disponibles, puede pasartar --help.