Zauberhaft

MSDOS- und Windows-Anwender sind es gewohnt, dass der Name einer Datei gleichzeitig ihren ‘Typ’ anzeigt. Auf Unix-Systemen konnte sich diese Konvention nur teilweise durchsetzen. In vielen Fällen zählt allein der Inhalt einer Datei, getreu dem von Goethe formulierten Motto: ‘Name ist Schall und Rauch’.

In Pocket speichern vorlesen Druckansicht 7 Kommentare lesen
Lesezeit: 13 Min.
Von
  • Michael Riepe
Inhaltsverzeichnis

Wer hat nicht schon einmal eine Datei aus dem WWW geladen oder per E-Mail geschickt bekommen, deren Dateiname keinen Aufschluss darüber gibt, wie mit ihr zu verfahren sei? Noch unangenehmer kann es sein, wenn der Name - versehentlich oder vorsätzlich - einen falschen Typ impliziert; auf diese Weise verbreiten sich unter anderem einige der E-Mail-Würmer, die seit jüngster Zeit im Internet ihr Unwesen treiben.

Bei der Identifizierung unbekannter Datenformate hilft das Programm file. Es untersucht zunächst die Attribute der als Argumente übergebenen Dateinamen, um festzustellen, ob es sich um ‘richtige’ Dateien (‘regular files’), Verzeichnisse, Gerätedateien, symbolische Links oder andere ‘special files’ handelt. Ist der Proband eine reguläre Datei, öffnet file sie und durchsucht ihren Inhalt nach bestimmten Bytefolgen - so genannten ‘Signaturen’ oder ‘magic numbers’ -, die das Format eindeutig identifizieren. Das Programm vergleicht den Dateiinhalt der Reihe nach mit jeder ihm bekannten Magic Number, bis es eine passende gefunden hat.

Bleibt die Suche nach einer bekannten Signatur erfolglos, startet file die dritte und letzte Stufe der Erkennungsprozedur. Zuerst untersucht es den Anfang der Datei - in der Regel einige KByte - Byte für Byte, um festzustellen, ob die Datei Text oder Binärdaten enthält. Findet es nicht druckbare Zeichen, klassifiziert es die Datei als ‘data’ - was etwa so viel heißt wie ‘unidentifizierbar’ -, und die Untersuchung ist beendet. Andernfalls versucht es, durch verschiedene Heuristiken - etwa durch Suchen nach bestimmten Schlüsselwörtern - herauszufinden, um welche Art Text es sich handelt - normales Englisch, Quelltexte in verschiedenen Programmiersprachen und so weiter. Zwar kann der Benutzer nicht davon ausgehen, dass file jede Textdatei korrekt erkennt, die Trefferrate des Programms ist jedoch erstaunlich hoch für einen so simplen Algorithmus. Will der Anwender es ganz genau wissen, muss er wohl oder übel die Datei mit einem Editor öffnen und selbst in Augenschein nehmen.

Nahezu jedes kommerzielle Unix besitzt seine eigene, unabhängig weiterentwickelte Version von file. Ihre Arbeitsweisen unterscheiden sich kaum, die Aufrufsyntax beziehungsweise die unterstützten Optionen hingegen erheblich. Eine GNU-Version des Programms existiert nicht, wohl aber eine freie, deren Quellcode unter einer BSD-ähnlichen Lizenz steht.

Normalerweise muss der Benutzer die Namen der zu untersuchenden Dateien als Argumente an das Programm übergeben. Die Option -f input weist das Programm an, die Dateinamen stattdessen aus der Datei input zu lesen. Die Anweisung file -f liest von der Standardeingabe, was zum Beispiel die Kombination mit dem Befehl find ermöglicht. Diese Option ist ebenso in der Solaris-Version des Programms zu finden. Dort existiert außerdem die Option -h, die bewirkt, dass file symbolische Links als solche erkennt und behandelt. In der freien Version ist dieses Verhalten voreingestellt; die Kompatibilität mit Solaris lässt sich mit der Option -L erzwingen.

Spezialitäten des freien Programms stellen die Optionen -k, -z und -s dar. Mit -k kann der Benutzer file anweisen, die Suche nach der passenden Magic Number bis zum Ende durchzuführen, anstatt sie beim ersten Treffer zu beenden - etwa wenn das Datenformat nicht ganz eindeutig ist. Verwendet er die Option -z, riskiert das Programm bei komprimierten Dateien zusätzlich einen Blick auf die ausgepackten Daten. Die Anweisung file -s schließlich versucht, Daten auch von Gerätedateien zu lesen - Linux-Benutzer können zum Beispiel mit file -s /dev/hda3 feststellen, welches Dateisystem auf der dritten Partition ihrer Festplatte installiert ist.

Die Magic Numbers aller bekannten Dateiformate entnimmt file einer Textdatei namens magic. Sie ist je nach Betriebssystem in einem der Verzeichnisse /etc, /usr/share oder /usr/share/misc zu finden; der Linux Filesystem Hierarchy Standard (FHS) 2.2 favorisiert Letzteres. Bei einigen Versionen des Programms kann der Benutzer beim Aufruf mit der Option -m dateiname eine alternative Informationsquelle auswählen. Das freie file erlaubt sogar die Angabe mehrerer Dateinamen, getrennt mit Doppelpunkten, die es der Reihe nach durchsucht. Die Option -c dient dazu, eine neu erstellte oder geänderte Magic-Datei auf Fehler zu überprüfen.

Einträge in der Magic-Datei beanspruchen jeweils eine Textzeile und bestehen aus vier mit Leerzeichen oder Tabulatoren getrennten Feldern. Das erste Feld enthält eine ganze Zahl, die festlegt, an welcher Stelle der Datei die Magic Number zu finden ist, gezählt in Bytes vom Dateianfang. Oktal- und Hexadezimalzahlen werden wie in C/C++ üblich mit vorangestelltem ‘0’ beziehungsweise ‘0x’ geschrieben. Bei der Mehrzahl der Einträge hat dieser so genannte ‘Offset’ den Wert null - die Magic Number liegt am Dateianfang.

Im zweiten Feld erwartet file eine Typangabe, die Länge und Kodierung der Dateisignatur beschreibt. Fast alle Versionen des Programms unterscheiden die Typen ‘byte’, ‘short’ (2 Byte), ‘long’ 4 Byte) und ‘string’ (eine Zeichenkette mit variabler Länge). Leider ist die Reihenfolge, in der die Bytes eines Datums vom Typ ‘short’ oder ‘long’ gespeichert sind, von der verwendeten CPU abhängig; soll die Magic-Datei portabel sein, darf der Benutzer diese Typen nicht verwenden. Das freie file bietet als Ergänzung die Typen ‘beshort’ und ‘leshort’ beziehungsweise ‘belong’ und ‘lelong’ mit eindeutig festgelegter Reihenfolge an. Das Präfix ‘le’ steht für ‘little-endian’ - die niedrigste Adresse enthält das niederwertigste Byte -, ‘be’ für die umgekehrte Reihenfolge (‘big-endian’).

Die eigentliche Magic Number ist im dritten Feld gespeichert. Je nach Typ handelt es sich entweder um eine Zeichenkette oder einen ganzzahligen Ausdruck. In Zeichenketten sind die C-typischen Escape-Sequenzen erlaubt - ‘\n’ für Zeilenende, ‘\t’ für Tabulator, ‘\\’ für ‘\’ und so weiter. Strings werden jedoch ohne begrenzende Anführungszeichen geschrieben; Leerzeichen innerhalb des Strings muss der Benutzer als ‘\ ‘ schreiben, damit file sie nicht als Ende des Strings interpretiert.

Numerische Angaben können Zahlen oder einfache Ausdrücke sein. Ein vorangestelltes ‘<’ oder ‘>’ zeigt an, dass die Zahl in der Datei kleiner beziehungsweise größer sein muss als der angegebene Wert; ‘=’ (gleicher Wert) ist die Standardeinstellung. Die Operatoren ‘&’ und ‘^’ repräsentieren bitweise Vergleiche. Steht ein ‘&’ vor dem Wert, müssen in der Datei alle Bits gesetzt sein, die auch in der angegebenen Zahl gesetzt sind; der Operator ‘^’ gibt sich zufrieden, wenn wenigstens eins der Bits den Wert null hat.

Der Rest des Eintrags enthält die Bezeichnung des Dateiformats - im Klartext. Passt der Eintrag zu der untersuchten Datei, gibt file diesen Text aus.

Zeilen, die mit ‘>’ beginnen, enthalten ‘sekundäre’ Einträge. Sie kommen nur zum Einsatz, nachdem file das Dateiformat identifiziert hat, und liefern zusätzliche Details - etwa Auflösung oder Farbtiefe bei Bilddateien. Jeder sekundäre Datensatz bezieht sich auf den vorangehenden ‘primären’ Eintrag (ohne führendes ‘>’). Die freie Programmversion unterstützt beliebig tief geschachtelte, voneinander abhängige Datensätze. Die Anzahl der Größer-Zeichen am Zeilenanfang gibt die Schachtelungstiefe an.

Sekundäre Datensätze sollen oft nicht den Inhalt der Datei prüfen, sondern nur die gefundenen Daten ausgeben. Zu diesem Zweck kann der Anwender in der vierten Spalte einen Platzhalter einfügen, den file durch den Dateiinhalt am angegebenen Offset ersetzt. Für Zeichenketten kommt als Platzhalter ‘%s’ infrage, numerische Werte lassen sich mit ‘%d’ (vorzeichenbehaftet) oder ‘%u’ (vorzeichenlos) darstellen. Für die Auswertung ist die C-Bibliotheksfunktion printf() zuständig; alle dort erlaubten Formatierungsanweisungen funktionieren ebenso im Magic-File. Der Benutzer sollte jedoch im Hinterkopf behalten, dass file im Gegensatz zu printf() nur einen einzigen Platzhalter erlaubt. Überzählige Formatierungsanweisungen führen dazu, dass das Programm scheinbar bedeutungslose Daten ausgibt, schlimmstenfalls sogar abstürzt. Läuft das Programm mit Superuser-Rechten, stellt eine fehlerhafte Magic-Datei unter Umständen ein Sicherheitsrisiko dar.

Die HPUX-Variante von file stand offenbar Pate für ein anderes nützliches Feature: Schreibt der Benutzer den Offset in runde Klammern, liest das Programm den Dateiinhalt an der angegebenen Stelle und verwendet den gelesenen Wert als Offset. Ein nachgestelltes ‘.b’, ‘.s’ oder ‘.l’ teilt file mit, wie viele Bytes die Zahl beansprucht; die Default-Einstellung ist ‘.l’ (‘long’, 4 Byte). Konsequenterweise kann man bei der freien Version ebenso wie oben zwischen Big- und Little-Endian-Format wählen; Großbuchstaben stehen für das Big-Endian-Format. Auf den gelesenen Wert kann der Nutzer vor der Verwendung noch eine Konstante addieren lassen.

Bei jedem Start liest file seine magische Datenbank neu ein und transformiert sie in eine interne Darstellung, um bei der Suche nach dem passenden Eintrag Zeit zu sparen. Diese ‘Lernphase’ zahlt sich aus, wenn das Programm viele Dateien nacheinander untersuchen muss. Ruft der Benutzer file mit nur einem Dateinamen auf - etwa innerhalb einer Schleife -, verbringt das Programm jedoch mehr Zeit damit, die Magie zu erlernen, als sie anzuwenden.

Der Umfang der Datenbank wächst ständig; bei der freien Programmversion umfasst sie gegenwärtig rund 4450 Einträge. Der Anwender kann die Startzeit des Programms verkürzen, indem er alle nicht benötigten Datensätze und Kommentare aus der Datei magic entfernt. Hat der Benutzer kein Schreibrecht für die Datei, sollte er eine Kopie anlegen und bearbeiten. Die Shell-Befehle

MAGIC=$HOME/magic.privatexport MAGIC

veranlassen file, statt der öffentlichen Datenbank die Datei magic.privat im Home-Directory des Benutzers zu verwenden. Analog zur Option -m ist die Angabe mehrerer Dateinamen erlaubt.

Schreibt der Anwender die Anweisungen in die Shell-Startdatei $HOME/.profile, weist die Shell der Variablen MAGIC beim Login automatisch den gewünschten Wert zu. Bash-Benutzer sollten ~/.bash_profile verwenden, falls diese Datei existiert; Fans der C-Shell und der tcsh müssen die Zeile setenv MAGIC ~/magic.privat in die Datei ~/.login eintragen, um dieselbe Wirkung zu erzielen.

Die aktuelle Version 3.37 des Programms sucht von sich aus nach einer privaten Datei des Benutzers, unter dem Namen $HOME/.magic - allerdings ist dieses Feature nicht dokumentiert. Hochoffiziell ist hingegen die Fähigkeit, eine ‘vorkompilierte’ Version der Datenbank zu verwenden. Der Befehl file -C liest die Textversion der gewünschten Magic-Datei, übersetzt sie ins interne Format und schreibt das Ergebnis in eine zweite Datei. Die kompilierte Datenbank ist an der Namensendung ‘.mgc’ erkennbar; file kann sie aber auch anhand ihrer Magic Number identifizieren (siehe Listing 1).

Mehr Infos

Listing 1

Selbsterkenntnis: Wie das Experiment am eigenen Quelltext beweist, identifiziert file verschiedene Arten von Textdateien mit erstaunlich hoher Treffsicherheit. Lediglich das Makefile hat das Programm nicht richtig erkannt.

$ file README Makefile configure* file* magic.mgc
README: ASCII English text
Makefile: ASCII English text, with very long lines
configure: Bourne shell script text executable
configure.in: ASCII M4 macro language pre-processor text
file: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped
file.1: ASCII troff or preprocessor input text
file.c: ASCII C program text
file.h: ASCII C program text
file.man: ASCII troff or preprocessor input text
file.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
magic.mgc: magic binary file for file(1) cmd (version 1) (little endian)

Vorkompilierte Magic-Dateien verkürzen die Lernphase des Programms erheblich, haben jedoch einen Nachteil: Der Benutzer muss sie neu generieren, nachdem er das Original editiert hat - vergisst er das, bleiben seine Änderungen wirkungslos. Auf die Textversion greift file nur noch zurück, wenn keine vorkompilierte Datei existiert; die lästige Warnung, die das Programm in diesem Fall ausgibt, dürfte wahrscheinlich viele nötigen, ihre Magic-Dateien zu kompilieren. Eleganter wäre es, die neuere der beiden Dateien zu verwenden und - entsprechende Berechtigung vorausgesetzt - die kompilierte Version automatisch neu zu erstellen, falls die Textdatei aktueller ist.

Seit der Einführung der Multipurpose Internet Mail Extensions (MIME) ist es üblich, Dokumenten einen so genannten ‘content type’ zuzuordnen, der ihren Inhalt genauer beschreibt. Der Content-Type besteht aus zwei Teilen; der erste unterscheidet grob zwischen Text, Bildern (‘image’), Audio- und Videodateien und anderen Binärdaten (‘application’) sowie zusammengesetzten Dokumenten (‘multipart’) und E-Mails (‘message’). Dahinter folgt, mit einem Schrägstrich abgetrennt, die genaue Bezeichnung des Dateiformats - zum Beispiel ‘text/plain’, ‘image/jpeg’, oder ‘application/octet-stream’. Letzteres steht für Dateien, die sich nicht in eine der anderen Kategorien einordnen lassen. Beginnt der Untertyp mit den Zeichen ‘x-’, handelt es sich um eine inoffizielle, undokumentierte Erweiterung. Anwendungen, die den MIME-Typ nicht erkennen, müssen die Datei stillschweigend so behandeln, als lautete ihr Typ ‘application/octet-stream’ [1].

Der Benutzer kann sich den MIME-Dateityp mit der Option -i anzeigen lassen. Sie veranlasst file unter anderem, eine andere Magic-Datei zu verwenden; sie trägt den Namen magic.mime und liegt im gleichen Verzeichnis wie die Standarddatenbank. Die Optionen -i und -m lassen sich gemeinsam verwenden, man muss allerdings die Reihenfolge beachten: Während der Befehl file -i -m ~/my.magic die Datei ~/my.magic benutzt, erwartet die Anweisung file -m ~/my.magic -i eine Datei namens ~/my.magic.mime.

Funktionen beziehungsweise C++-Klassen, die aus dem Inhalt einer Datei ihren MIME-Typ ableiten, sind Bestandteil der Laufzeitbibliotheken von GNOME und KDE. Beide Desktops verwenden jedoch ihre eigenen Datenbanken, statt auf die von file zurückzugreifen. Eine von KDE, GNOME und file gemeinsam genutzte Bibliothek könnte diesem Chaos ein Ende bereiten; das setzt allerdings voraus, dass die Entwickler aller drei Softwarepakete miteinander kooperieren.

Die Unterschiede zwischen den file-Varianten machen sich besonders in einer heterogenen Arbeitsumgebung störend bemerkbar. Vor allem Benutzer, die file in Shellskripts einsetzen, tun gut daran, auf allen Systemen die portable freie Version zu installieren. Der Quelltext steht - einschließlich Datenbank und Dokumentation - zum Download bereit.

Michael Riepe
studiert Elektrotechnik an der Universität Hannover.

[1] N. Freed, N. Borenstein; Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types; RFC 2046; November 1996;

Mehr Infos

iX-TRACT

  • Zum Feststellen von Datentypen bietet file eine Vielzahl von Möglichkeiten.
  • Anwender können mit eigenen Einträgen in magic-Dateien weitere neue Formate hinzufügen.
  • Das Kommando file ermittel mit der Option -i auch den Mime-Typ von Dateien.
Mehr Infos

Listing 2

Magie für Einsteiger: Nicht alle Dateiformate sind so unkompliziert wie diese. Das 'gzip'-Format etwa benötigt 22 Datensätze, für ausführbare Dateien im verbreiteten ELF-Format sind 166 Zeilen notwendig.

257     string          ustar\0         POSIX tar archive
257 string ustar\040\040\0 GNU tar archive

0 string MZ MS-DOS executable (EXE)

0 belong 0xcafebabe compiled Java class data,
>6 beshort x version %d.
>4 beshort x \b%d

0 string %PDF- PDF document
>5 byte x \b, version %c
>7 byte x \b.%c

(rh)