Sparse files: posible ahorro de espacio
Una vez más, Andries Brouwer en su asignatura me ha enseñado algo interesante, esta vez incluso sin ir a clase pues estaba en España. Tras el ejercicio anterior de las syscalls, y otro que no posteé porque no lo consideraba interesante ( explotar una format string normal y corriente, sin protecciones tipo va_patch ni nada... ) esta vez toca crear una utilidad que copie un archivo normal a un 'sparse file'.
Y qué es eso? Pues según wikipedia :
In computer science, a sparse file is a type of computer file that attempts to use file system space more efficiently. When space has been allocated to a file but not actually filled with data it is not written to the file system. Instead, meta-information about these "empty" regions is stored until they are filled with data.
File systems supporting sparse files include: VxFS, Apple DOS, CP/M, NTFS, ext2, ext3, GPFS, XFS, JFS, ReiserFS, Reiser4, UFS, ZFS, VMFS
Y cómo creamos uno de estos archivos a partir de su equivalente 'normal' ? Pues muy sencillo: cuando leemos un bloque de 0's, en lugar de escribirlo en disco lo que haremos será un lseek para avanzar el puntero esas posiciones. En mi caso, lo que he hecho ha sido leer byte a byte y hacer el lseek si el byte leído era 0 y hacer un write si no lo era. Algo así:
while( (n=read(ifd,&buf,1))>0 ){
if(buf==0){
if(lseek(ofd,n,SEEK_CUR)==-1)
goto error;
} else {
if(write(ofd,&buf,n)==-1)
goto error;
}
}
En este caso, ifd es el descriptor del archivo de entrada, ofd del de salida, y buf es un char. Seguramente sería más eficiente leer bloque a bloque, y para ello tenemos dos opciones: leer un bloque de tamaño fijo o bien averiguar el tamaño de bloque del dispositivo de salida. Para lo primero yo he usado un simple #define, y me he creado un buffer auxiliar para rellenarlo con 0's usando memset( ) y hacer la comparación con memcmp( ), derrochando 4k de memoria pero evitando hacer la comparación a mano que tenía pereza 😉
La segunda opción no la he implementado, pero existe la syscall [f]statfs que nos devuelve una estructura con información de un sistema de ficheros montado, entre ella el tamaño de bloque.
Para probar el programita yo lo que he hecho es lo siguiente:
~/work/lk_hh $ dd if=/dev/urandom of=my_no_zeros bs=4096 count=22+0 records in
2+0 records out
8192 bytes (8,2 kB) copied, 0,0042889 s, 1,9 MB/s
~/work/lk_hh $ dd if=/dev/zero of=my_zeros bs=4096 count=5
5+0 records in
5+0 records out
20480 bytes (20 kB) copied, 0,000177221 s, 116 MB/s
~/work/lk_hh $ cat my_no_zeros my_zeros my_no_zeros my_zeros my_no_zeros > my_no_sparse_file
~/work/lk_hh $ ./cpsparse my_no_sparse_file my_sparse_file
~/work/lk_hh $ du -sh *sparse_file
64K my_no_sparse_file
24K my_sparse_file
~/work/lk_hh $
Se puede comprobar que en este caso se han ahorrado 40k, correspondientes a las diez veces que aparece un bloque entero de bytes nulos: cinco por cada vez que concatené el archivo my_zeros.
Esto de los sparse files podría ser interesante para archivos grandes como imágenes .iso o similares que pudieran contener bloques de bytes nulos, aunque no sé si en la práctica será útil o no, no he probado con archivos comunes. Alguien tiene idea? Alguna sugerencia sobre el código o algo que aportar al respecto?
PD: Lo de usar goto para el control de errores es lo que se hace en el kernel y sugerencia del profesor en su primera clase. Así tienes el control de errores localizado y no mil if's anidados por cada posible error del código. En la etiqueta error lo que se hace es el cleanup, en mi caso simplemente cierro los archivos y me cargo mediante unlink( ) el que he creado puesto que no es correcto.
September 25th, 2007 - 17:26
interesante!
September 30th, 2007 - 23:00
No tiene nada que ver, pero ha salido SET #34 y viene con un artículo que te gustará: 0x0d introducción a los buffer overflows en x86_64 por RaiSe.
October 1st, 2007 - 18:31
Gracias 😉 Aunque ahora he perdido un ratillo leyendolo rápido en lugar de aprovecharlo para hacer el trabajo que tengo que entregar mañana y no he empezado 😳 .
El tema está interesante. Las protecciones complican las cosas … saltar a las funciones via la PLT es una gran idea, aunque necesitamos que el programa vulnerable utilice alguna función que nos venga bien o que se pueda obtener información de alguna manera para localizar la libc.
Gracias de nuevo, voy a ver si le echo un ojo al tema del trabajo este.