2010-03-24 5 views
14

Ich möchte eine .tar-Datei mit mehreren Verzeichnissen mit je 2 Dateien herunterladen. Das Problem ist, ich kann keine Möglichkeit finden, die TAR-Datei zu lesen, ohne die Dateien tatsächlich zu extrahieren (mit tar).Wie Sie eine TAR-Datei in C++ analysieren

Die perfekte Lösung wäre so etwas wie:

#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 
} 

Ich werde wahrscheinlich haben diese selbst zu schreiben, aber alle Ideen würde geschätzt ..

+1

'man tar' sagt mir' -t Listet den Archivinhalt auf "stdout" auf. Ist das was du willst? – Potatoswatter

+1

Was ich eigentlich will, ist das Gegenteil: eine TAR-Datei von der Standardeingabe lesen. –

Antwort

29

ich das selbst von der Arbeit nach etwas herausgefunden. Die tar file spec sagt Ihnen eigentlich alles, was Sie wissen müssen.

Als Erstes beginnt jede Datei mit einem 512-Byte-Header, so dass Sie sie mit einem char [512] oder einem char * an einer beliebigen Stelle in Ihrem größeren char-Array darstellen können (wenn Sie die gesamte Datei in ein Array laden) beispielsweise).

Der Header sieht wie folgt aus:

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 

Also, wenn Sie den Dateinamen möchten, können Sie es gleich hier mit string filename(buffer[0], 100); greifen. Der Dateiname ist null, daher können Sie eine Überprüfung durchführen, um sicherzustellen, dass mindestens eine Null vorhanden ist. Wenn Sie Speicherplatz sparen möchten, müssen Sie die Größe nicht beibehalten.

Jetzt wollen wir wissen, ob es eine Datei oder ein Ordner ist. Die „Verbindungsanzeige“ Feld hat diese Informationen, so:

// 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; 
} 

An dieser Stelle haben wir bereits alle Informationen, die wir über Verzeichnisse benötigen, aber wir brauchen noch eine Sache von normalen Dateien: die tatsächlichen Dateiinhalte.

Die Länge der Datei kann auf zwei verschiedene Arten gespeichert werden, entweder als Null-terminierte oktale Zeichenfolge mit 0 oder Leerzeichen oder als "Basis-256-Codierung", die durch Setzen des höherwertigen Bits angezeigt wird des linken Bytes eines numerischen Feldes ".

Numerische Werte werden in Oktalzahlen mit ASCII-Ziffern und führenden Nullen kodiert. Aus historischen Gründen sollte ein endgültiges NUL- oder Leerzeichen verwendet werden. Obwohl 12 Bytes zum Speichern der Dateigröße reserviert sind, können daher nur 11 Oktalstellen gespeichert werden.Dies ergibt eine maximale Dateigröße von 8 Gigabyte für archivierte Dateien. Um diese Einschränkung zu überwinden, führte Star im Jahr 2001 eine Basis-256-Kodierung ein, die durch Setzen des höherwertigen Bits des am weitesten links liegenden Bytes eines numerischen Feldes angezeigt wird. GNU-tar und BSD-tar folgten dieser Idee. Darüber hinaus packen die Versionen von tar vor dem ersten POSIX-Standard von 1988 die Werte mit Leerzeichen anstelle von Nullen.

Hier ist, wie Sie das Oktalformat lesen würde, aber ich habe keinen Code für die Basis-256-Version geschrieben:

// 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, so haben wir jetzt alles außer den tatsächlichen Dateiinhalte. Alles, was wir tun müssen, ist das nächste size Bytes von Daten aus der TAR-Datei packen und wir werden unseren Dateiinhalt haben:

// 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

Ich verwende derzeit Ihren Code, und für TAR-Dateien, die mit Gnome File Roller erstellt wurden, scheint "sizeOfFile = octalStringToInt (..., 11)" in einigen seltenen Fällen falsch zu sein. Können Sie darauf hinweisen, welche "Magie" im 12. Byte weggelassen wurde? – rodrigob

+0

@rodrigob Ich weiß es wirklich nicht. Wenn Sie es herausfinden, lassen Sie es mich wissen. –

+0

Hinweis, wenn die Dateigröße ist _exactly_ 512 Bytes, dann 'location = location + ((Größe/512) + 1) * 512 'wird nächsten Header verpassen –

11

Haben Sie bei libtar geschaut?

Vom fink Paketinformationen:

libtar-1.2-1: Tar-Datei Manipulation API libtar für die Manipulation von POSIX-tar-Dateien eine C-Bibliothek ist. Es behandelt Hinzufügen und Extrahieren von Dateien zu/von einem tar-Archiv. libtar bietet die folgenden Funktionen:
* Flexible API - Sie können einzelne Dateien oder einfach nur ein ganzes Archiv auf einmal zu manipulieren.
* Ermöglicht benutzerdefinierte lese() - und write() -Funktionen, z. B. zlibs gzread() und gzwrite().
* Unterstützt POSIX 1003.1-1990 und GNU TAR-Dateiformate.

Nicht C++ per se, aber man kann c ziemlich leicht verbinden ...

+3

Die Dokumentation Art von saugt, aber ich überprüfe es aus .. –

+0

@ BrendanLong King of sucks ist eine Übertreibung. –

4

libarchive kann die Open-Source-Bibliothek sein, den Tarball zu analysieren. Libarchive kann alle Dateien aus einer Archivdatei ohne Extraktion lesen und auch Daten in eine neue Archivdatei schreiben.

Verwandte Themen