2010-03-24 21 views
14

Lo que quiero hacer es descargar un archivo .tar con varios directorios con 2 archivos cada uno. El problema es que no puedo encontrar una manera de leer el archivo tar sin realmente extraer los archivos (usando tar).Cómo analizar un archivo tar en C++

La solución perfecta sería algo así como:

#include <easytar> 

Tarfile tar("somefile.tar"); 
std::string currentFile, currentFileName; 
for(int i=0; i<tar.size(); i++){ 
    file = tar.getFileText(i); 
    currentFileName = tar.getFileName(i); 
    // do stuff with it 
} 

probablemente voy a tener que escribir esto por mí mismo, pero cualquier idea sería apreciada ..

+1

'man tar' me dice' -t Listar los contenidos del archivo a stdout. ¿Eso es lo que quieres? – Potatoswatter

+1

Lo que realmente quiero es lo opuesto: leer un archivo tar desde stdin. –

Respuesta

29

Me di cuenta de esto a mí mismo después de un poco de trabajo. El tar file spec realmente le dice todo lo que necesita saber.

En primer lugar, cada archivo comienza con una cabecera de 512 bytes, por lo que puede representar con un char [512] o un char * que apunta a algún lugar de su matriz de caracteres de mayor tamaño (si tiene el archivo completo cargado en una matriz por ejemplo).

La cabecera tiene el siguiente aspecto:

location size field 
0   100 File name 
100  8  File mode 
108  8  Owner's numeric user ID 
116  8  Group's numeric user ID 
124  12 File size in bytes 
136  12 Last modification time in numeric Unix time format 
148  8  Checksum for header block 
156  1  Link indicator (file type) 
157  100 Name of linked file 

lo tanto, si desea que el nombre del archivo, lo agarras aquí con string filename(buffer[0], 100);. El nombre del archivo es nulo, por lo que puede hacer una comprobación para asegurarse de que haya al menos un nulo y luego dejar el tamaño si desea ahorrar espacio.

Ahora queremos saber si es un archivo o una carpeta. El campo "indicador de enlace" tiene esta información, por lo que:

// Note that we're comparing to ascii numbers, not ints 
switch(buffer[156]){ 
    case '0': // intentionally dropping through 
    case '\0': 
     // normal file 
     break; 
    case '1': 
     // hard link 
     break; 
    case '2': 
     // symbolic link 
     break; 
    case '3': 
     // device file/special file 
     break; 
    case '4': 
     // block device 
     break; 
    case '5': 
     // directory 
     break; 
    case '6': 
     // named pipe 
     break; 
} 

En este punto, ya tenemos toda la información que necesitamos sobre directorios, pero necesitamos una cosa más de los archivos normales: el contenido real de los archivos.

La longitud del archivo se puede almacenar de dos maneras diferentes, ya sea como una cadena octal terminada en cero 0 o espacio-acolchada, o "una codificación base-256 que se indica configurando el bit de orden superior del byte más a la izquierda de un campo numérico ".

Los valores numéricos están codificados en números octales utilizando dígitos ASCII, con ceros a la izquierda. Por razones históricas, se debe usar un carácter NUL o espacio final. Por lo tanto, aunque hay 12 bytes reservados para almacenar el tamaño del archivo, solo se pueden almacenar 11 dígitos octales.Esto proporciona un tamaño de archivo máximo de 8 gigabytes en archivos archivados. Para superar esta limitación, star en 2001 introdujo una codificación de base-256 que se indica configurando el bit de orden superior del byte más a la izquierda de un campo numérico. GNU-tar y BSD-tar siguieron esta idea. Además, las versiones de tar antes del primer estándar POSIX de 1988 rellenan los valores con espacios en lugar de ceros.

Aquí es cómo se lee el formato octal, pero no han de código escrito para la versión base-256:

// in one function 
int size_of_file = octal_string_to_int(&buffer[124], 11); 

// elsewhere 
int octal_string_to_int(char *current_char, unsigned int size){ 
    unsigned int output = 0; 
    while(size > 0){ 
     output = output * 8 + *current_char - '0'; 
     current_char++; 
     size--; 
    } 
    return output; 
} 

Ok, así que ahora tenemos todo excepto el contenido real de los archivos. Todo lo que tenemos que hacer es agarrar los próximos size bytes de datos del archivo de alquitrán y tendremos nuestros contenidos de archivo:

// Get to the next block after the header ends 
location += 512; 
file_contents = new char[size]; 
memcpy(file_contents, &buffer[location], size); 
// Go to the next block by rounding up to 512 
// This isn't necessarily the most efficient way to do this, 
// but it's the most obvious. 
location += (int)ceil(size/512.0) 
+0

Actualmente estoy usando su código, y para archivos tar creados con Gnome File Roller, el "sizeOfFile = octalStringToInt (..., 11)" parece ser incorrecto "en algunos casos raros". ¿Podría señalar qué fue la "magia" omitida en el byte 12? – rodrigob

+0

@rodrigob Realmente no lo sé. Si lo descubres, déjame saber. –

+0

Nota, si el tamaño del archivo es _exactly_ 512 bytes, entonces 'location = location + ((size/512) + 1) * 512' se perderá el próximo encabezado –

11

¿Has mirado en libtar?

A partir de la información del paquete Fink:

libtar-1.2-1: archivo tar la manipulación API libtar es una biblioteca de C para manipular archivos POSIX alquitrán. Maneja agregando y extrayendo archivos a/desde un archivo tar. libtar ofrece las siguientes características:
* API flexible: puede manipular archivos individuales o simplemente extraer un archivo completo a la vez.
* Permite funciones de lectura() y de escritura() especificadas por el usuario, como zzib's gzread() y gzwrite().
* Admite formatos de archivos tar de POSIX 1003.1-1990 y GNU.

No C++ per se, sino que puede enlazar con bastante facilidad c ...

+3

La documentación es una mierda, pero lo estoy comprobando ... –

+0

@BrendanLong King of Sucks es una exageración. –

4

libarchive puede ser la biblioteca de código abierto para analizar el archivo comprimido. Libarchive puede leer cada archivo de un archivo de almacenamiento sin extracción, y también puede escribir datos para formar un nuevo archivo de almacenamiento.

Cuestiones relacionadas