Si la velocidad es importante y no se necesita compresión, puede enganchar los envoltorios de syscall que se tar
usan LD_PRELOAD
, para cambiarlos tar
y 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 read
y write
eso se realiza en la operación normal de tar
. Esto es tar
mucho 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
, read
y write
funciones POSIX. La macro OUT_FD
controla qué descriptor de archivo esperamos tar
usar como archivo de salida. Actualmente está configurado en stdout.
read
se cambió para devolver el valor de éxito de count
bytes 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.
write
se modificó para sumar los count
bytes de entrada en la variable global total
y devolver el valor de éxito de los count
bytes solo si el descriptor de archivo coincide OUT_FD
, de lo contrario, llama al reiniciador original adquirido a través dlsym
de realizar la llamada al sistema del mismo nombre.
close
todavía realiza todas sus funciones originales, pero si el descriptor de archivo coincide con OUT_FD, sabe que tar
se ha intentado escribir un archivo tar, por lo que el total
nú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_PRELOAD
solució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_PRELOAD
té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/
--totals
opció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
.