Hadmut Danisch

Ansichten eines Informatikers

Fantomas und das vfat-Dateisystem

Hadmut
12.5.2013 15:51

Kleines Update zum Fantomas-Festplatten-Artikel und ein paar technische Anmerkungen.

Nachdem so viele Leute zum vorangegangenen Artikel über die von Fantomas sabotierte Festplatte geantwortet haben, will ich mal meine Erkenntnisgewinne weitergeben.

Also: Die einfachste Variante wäre sicherlich gewesen, unter Windows ein Dateilisting zu machen, ein chkdsk laufen zu lassen, und die umbenannten Dateien dann hinterher über Datum und Namen zuzordnen.

Aber ich wollt’s jetzt halt einfach wissen, und mir schon immer mal vfat näher anschauen. Ich habe mir zwar mal eine frühere Version des Datenformates detailliert und in jedem Piep angesehen, aber das war – allen Ernstes – vor 32(!) Jahren. Damals hab ich nämlich als Schüler das Betriebssystem eines Mitsui Sort M23-Mark III über die Osterferien disassembliert und komplett in Assembler durchgelesen und verstanden (damals ging sowas noch), mir dabei auch das etwas schräge Disketten-Dateisystem angeguckt (klar, wenn man die Software dazu liest), und bei der Gelegenheit auch das Dateisystem des alternativ dazu erhältlichen CP/M angesehen. Und das MSDOS-Dateisystem war letztlich nicht viel anderes, als ein abgekupfertes CP/M, das im wesentlichen erst mal um verschachtelte Directories erweitert worden war. Seither hab ich da auf Bit-Ebene nicht mehr viel mit gemacht, und wollte mir jetzt mal anschauen, wie weit sie gekommen sind. (Nein, viel weiter ist es nicht.)

Ein wesentlicher Unterschied zwischen der Unix-Systematik und dem vfat-Filesystem ist übrigens, dass bei Unix die Attribute wie Größe, Datum usw. nicht im Directory selbst, sondern am inode stehen, weshalb unter Unix ein ls -l grundsätzlich mehrere Aktionen braucht, nämlich zunächst das Auslesen eines Directories, bei dem man aber nur den Dateinamen und die inode-Nummer bekommt (mehr steht ja auch nicht drin), und dann über einen separaten stat bzw. lstat-Zugriff die Dateidaten holt. Und dieser zweite Zugriff schlägt grundsätzlich fehl, weil Unix dann die eben noch aufgelistete Datei beim Suchen nicht wiederfindet. Das sieht dann so aus (Ausgabe von ls -l):

-????????? ? ?      ?               ?            ? neo/KiKA_20130510_235000_01.ts
-????????? ? ?      ?               ?            ? neo/KiKA_20130510_235000.ts
-????????? ? ?      ?               ?            ? neo/KiKA_20130511_013500.ts
-????????? ? ?      ?               ?            ? neo/KiKA_20130511_030500.ts

Unter Windows hingegen (ich kenne die Windows-API jetzt nicht auswendig, müsst ich im Detail erst nachlesen) werden die Daten offenbar in einem Rutsch ausgelesen, denn im vfat stehen die Daten wie Datum, Größe usw. direkt mit im Directory-Eintrag, und stehen grundsätzlich mit zur Verfügung, wenn der Name gelesen wurde. Deshalb sieht ein dir unter Windows/cmd dann so aus:

10.05.2013  23:50     2.417.278.976 neo/KiKA_20130510_235000.ts
11.05.2013  01:26       223.068.160 neo/KiKA_20130510_235000_01.ts
11.05.2013  01:35        50.331.648 neo/KiKA_20130511_013500.ts
11.05.2013  03:05     2.382.348.288 neo/KiKA_20130511_030500.ts

(Wobei man übrigens an den stark unterschiedlichen Dateigrößen schon sieht, dass der Recorder aufeinanderfolgende Sendungen nicht richtig trennen kann, aber das hat mit unserem Problem hier nichts zu tun.)

Die von manchen Lesern vorgeschlagenen Reparaturmethoden, die Zeichenketten mithilfe des Unix-Befehls strings oder mit dem Hexeditor zu suchen, schlagen übrigens fehl, denn die Namen stehen nicht einfach so dort herum. Das hängt damit zusammen, dass vfat noch die altertümlichen 8+3 Dateinamen verwendet und solche langen Namen über einen ganz üblen Hack speichert:

Vor dem 8+3 Dateinamen werden ungültige Directory-Einträge erzeugt, deren Felder dafür missbraucht werden, Bruchstücke des langen Dateinamens abzulegen. Die findet man also mit normalen Suchfunktion gar nicht. Das hat im wesentlichen drei Gründe:

  • Der erste Grund ist, dass der Name in der Codierung UCS-2 abgelegt ist, die der Vorläufer von UTF-16 war. In little-endian codiert. Jedes Zeichen in zwei Byte.
  • Der zweite Grund ist, dass der Dateiname nicht am Stück abgelegt ist, sondern durch den Missbrauch ungültiger Directory-Einträge solche Felder verwendet werden müssen, bei denen kein Schaden entsteht. Deshalb wird der Name pro Directory-Eintrag auf drei Felder zu 5, 6 und 2 UCS-2-Zeichen (zu je zwei Byte) aufgeteilt, also zerrissen.
  • Und wenn diese insgesamt 13 Zeichen pro missbraucht-ungültigem Directory-Eintrag noch nicht reichen, werden eben mehrere solcher Einträge verwendet, und hier erfolgt das in umgekehrter Reihenfolge.

Das macht dann für die Datei “neo/KiKA_20130510_235000.ts” insgesamt vier Directory-Einträge, die im Hexdump dann so aussehen (je 32 Byte bzw. zwei Zeilen sind ein Directory-Eintrag):

0000400: 4373 0000 00ff ffff ffff ff0f 005e ffff  Cs...........^..
0000410: ffff ffff ffff ffff ffff 0000 ffff ffff  ................

0000420: 0230 0035 0031 0030 005f 000f 005e 3200  .0.5.1.0._...^2.
0000430: 3300 3500 3000 3000 3000 0000 2e00 7400  3.5.0.0.0.....t.

0000440: 016e 0065 006f 002f 004b 000f 005e 6900  .n.e.o./.K...^i.
0000450: 4b00 4100 5f00 3200 3000 0000 3100 3300  K.A._.2.0...1.3.

0000460: 4e45 4f5f 4b49 7e31 5453 2000 0000 40be  NEO_KI~1TS ...@.
0000470: aa42 aa42 6700 40be aa42 ac27 00c0 1490  .B.Bg.@..B.'....

Im vierten Eintrag ist in normalen ASCII der 8+3-Name NEO_KI~1.TS abgelegt. Der ist also harmlos.

Wie man dann sieht, ist der lange Name “neo/KiKA_20130510_235000.ts” über die vorangehenden drei Einträge verteilt, jeweils in 16 Bit codiert und aufgespalten. Eine gewöhnliche Suche hätte also nicht ohne weiteres geholfen, entwender muss man dazu nach dem 8+3-Namen suchen oder – was kein mir bekanntes bestehendes Suchprogramm so kann – den langen Namen eben so codieren und dann selektiv nach Bytefolgen in diesen Abständen suchen.

Und bemerkenswerterweise listet Linux diesen kurzen 8+3-Dateinamen bei einem vfat-gemounteten Verzeichnis zwar nicht auf, aber wenn man ihn mal weiß, kann man über diesen 8+3-Namen ganz normal auf die Video-Datei zugreifen und sie abspielen, ohne irgendetwas am Dateiverzeichnis geändert zu haben:

% dir 'NEO_KI~1.TS'
-rw-r--r-- 1 hadmut hadmut 2417278976 Mai 10 23:50 NEO_KI~1.TS

Müsste also auch normal verwendbar sein, wenn man Unix/Linux dazu bringt, das Dateisystem ohne die langen Namen zu mounten. Und sogar ein Umbenennen müsste jetzt möglich sein, wenn man als Quelle einfach den kurzen Namen angibt. Und in der Tat, ein simples

mv 'NEO_KI~1.TS' neo_KiKA_20130510_235000.ts

löst das Problem unter Linux ohne jeden Eingriff auf Block-Ebene bzw. mit dem Hex-Editor. Die Datei ist nun ganz normal lesbar:

-rw-r--r-- 1 hadmut hadmut 2417278976 Mai 10 23:50 neo_KiKA_20130510_235000.ts

Was vermutlich die eleganteste Methode sein dürfte. Übrigens sieht der Dateieintrag dann so aus:

0000600: 4373 0000 00ff ffff ffff ff0f 00fe ffff  Cs..............
0000610: ffff ffff ffff ffff ffff 0000 ffff ffff  ................

0000620: 0230 0035 0031 0030 005f 000f 00fe 3200  .0.5.1.0._....2.
0000630: 3300 3500 3000 3000 3000 0000 2e00 7400  3.5.0.0.0.....t.

0000640: 016e 0065 006f 005f 004b 000f 00fe 6900  .n.e.o._.K....i.
0000650: 4b00 4100 5f00 3200 3000 0000 3100 3300  K.A._.2.0...1.3.

0000660: 4e45 4f5f 4b49 7e35 5453 2000 0000 40be  NEO_KI~5TS ...@.
0000670: aa42 ac42 6700 40be aa42 ac27 00c0 1490  .B.Bg.@..B.'....

Problem gelöst. Und mein Skript (das sehr wüst quick and very dirty zusammengegurkt war, um das erstmal zu verstehen, das aber einiges über den Aufbau mitteilt) kann nichts kaputtmachen, weil es ausschließlich lesend zugreift.

Wer mehr wissen will: Als Quelle zum Verständnis habe ich diese Dokumentation und diesen Hinweis verwendet. Ab und zu mal so ne kleine Fingerübung, um die Knochen zu bewegen, ist gar nicht verkehrt.

Allerdings merkt man dann sehr schnell, dass das vfat-System ein ziemlicher Murks ist, weil man im Zwang der Rückwärtskompatibilität einen ziemlichen Mist aufgehäuft und solche üblen Krücken wie das Verstecken von Dateinamen in eigentlich ungültigen Einträgen vorgenommen hat. Was ein Scheiß! Richtig gruselig ist ja, dass die jetzt auch noch von Herstellern von Kameras, MP3-Playern usw. Lizenzgebühren haben wollen. Für diesen Schrott!


Nachtrag: Weil hier ein paar Schlaumeier meinten, man könnte das mal eben mit Hexeditor oder gar sed (Wahnsinn – mit sed im Dateisystem rumnudeln) ändern: Die langen vfat-Namen enthalten eine Prüfsumme über den kurzen 8+3-Namen. Wenn man den ändert, stimmt die Prüfsumme nicht mehr. So trivial ist es also nicht.

22 Kommentare (RSS-Feed)

Robert W.
12.5.2013 18:40
Kommentarlink

“Nachtrag: Weil hier ein paar Schlaumeier meinten, man könnte das mal eben mit Hexeditor oder gar sed …” 😉 das mit sed war nur einer (ich ;-), weil sed halt schon Muster ersetzen kann …).

Aber du hast recht, VFAT hat lange und kurze Dateinamen, die kurzen per hexeditor etc. zu ändern würd gehen, löst aber das Problem nicht (und im konkreten Fall war das ‘/’ im kurzen Dateinamen ja wohl eh nicht enthalten …).
Daß Linux auch mit den kurzen Dateinamen umgehen kann, ist ne interessante Info.


gedankenwerk
12.5.2013 18:46
Kommentarlink

Weil hier ein paar Schlaumeier meinten, man könnte das mal eben mit Hexeditor…

Du Schlaumeier meintest, dass es prinzipiell schon vom Userspace aus nicht ginge 😉 Damit fiele die normale Umbenennung aus.

Und die Prüfsumme des langen Namens wird aus dem kurzen Dateinamen berechnet, wenn die nicht stimmt, ist einfach der lange Name ungültig.
Aber auch die zu berechnen ist kein Ding der Unmöglichkeit.


Martok
12.5.2013 18:48
Kommentarlink

Wer sich dafür interessiert, findet auch bei Holger noch einiges an Erklärungen und Implementationen (dort für AVR):
http://www.holger-klabunde.de/avr/avrboard.htm#FullFAT


Robert W.
12.5.2013 20:02
Kommentarlink

PS, habs grad ausprobiert:
Unter linux mal ein kleines VFAT-Image “vfat.img” erzeugt, und dann:

# mount -t vfat -o loop vfat.img mnt
# date > mnt/Hallo_das_ist_ein_langer_Dateiname.txt
# date > mnt/Hallo_das_ist_noch_ein_langer_Dateiname.txt
# ls mnt
Hallo_das_ist_ein_langer_Dateiname.txt Hallo_das_ist_noch_ein_langer_Dateiname.txt
# umount mnt
# mount -t msdos -o loop vfat.img mnt
# ls mnt
hallo_~1.txt hallo_~2.txt
# mv mnt/hallo_~1.txt mnt/abc.txt
# umount mnt
# mount -t vfat -o loop vfat.img mnt
# ls mnt
ABC.TXT Hallo_das_ist_noch_ein_langer_Dateiname.txt
# umount mnt
# sed –in-place ‘s/HALLO/HALLP/;’ vfat.img
# mount -t vfat -o loop vfat.img mnt
# ls mnt
ABC.TXT HALLP_~2.TXT

Ciao


Hadmut
12.5.2013 20:09
Kommentarlink

Erstens ist das ziemlicher Wahnsinn, ein Dateisystem mit sed bearbeiten zu wollen. Das ist was für völlig Verrückte, die es nicht stört, ihr Dateisystem komplett zu schrotten. Wer sagt Dir denn, dass HALLO nicht zufällig in irgendeiner Videodatei oder in wichtigen Dateisystemstrukturen vorkommt?

Zweitens löst es das Problem insofern nicht, weil man mit diesem sed nur die kurzen Dateinamen ändert, das Problem aber in den langen besteht.

Drittens korrumpierst Du damit das Dateisystem, weil selbst dann, wenn es so funktioniert, wie Du Dir das vorstellst, die abgehängten Einträge für den langen Dateinamen übrig bleiben und nicht mehr zugehörig sind.

-> Ziemlicher Schrott und Murks. Sowas macht man einfach nicht.


Robert W.
12.5.2013 20:05
Kommentarlink

Nachtrag: den Tipp mit “mount -t msdos” hat gestern schon “ngl” gebracht.


Hab mal so eine Prüfsumme mit ein Zeichen erhöhen und ein anderes erniedrigen ausgetrixt. Ist doch egal, was da steht.

Carsten

“Ein Mann mit einer neuen Idee ist ein Scharlatan, bis er der Idee zum Erfolg verholfen hat.”
Mark Twain


Robert W.
12.5.2013 20:29
Kommentarlink

Mein Beispiel sollte nur zeigen, daß es prinzipiell geht (ein kleines Hack-Programm, das unter Berücksichtigung der FAT alle ‘/’ aus den Dateinamens-Einträgen, und nur daraus, durch ein ‘_’ ersetzt, sollte schnell geschrieben sein).
Den langen Dateinamen, den du eh nicht behalten willst, kannst du mit einer kleinen Edierung am 8.3-Dateinamen ungültig machen.

Die schnellste Lösung wär gewesen, das FS als “msdos” (und nicht “vfat”) zu mounten, solange der ‘/’ nicht auch schon im kurzen Dateinamen enthalten gewesen wäre, Datei umbenennen und auf die langen Dateinamen zu pfeifen.
(Sind dir die paar Bytes für die abgehängten Einträge der langen Dateinamen wirklich so wichtig? Dann halt: Dateien wegmoven, Directory entfernen, neu anlegen und die Dateien wieder zurückmoven. Sofern der vfat-Treiber das nicht eh schon selbständig berücksichtigt)


gedankenwerk
12.5.2013 20:30
Kommentarlink

Das Hex-Editoren und das direkte Eingreifen in Dateisysteme nichts für Unbedarfte ist, sollte eine Binsenweisheit sein.
Mit pauschalem Suchen / Ersetzen kann man sich natürlich prima ins Bein schießen.
Nur bei Informatikern geht man eigentlich davon aus, sich das nötige Hintergrundwissen vorher anzueignen und nicht am “Lebenden Objekt” zu experimentieren.
Eine schöne Übersicht fand ich:
http://members.inode.at/anton.zechner/az/FatFormat.htm


Joe
12.5.2013 20:34
Kommentarlink

In dem Fall sollte ein Filesystemcheck-Programm lediglich die ungültigen LFNs entfernen und das Problem wäre gelöst. Windows hat außerdem ein API, um “Shortnames” und LFNs getrennt behandeln zu können.

Die sog. “vfat”-Implementierung unter Linux ist nicht sauber gelöst. FAT hat keine Inodes (er nimmt einfach den FAT-Startsektor als virtuelle Inodenummer) und die Shortnames werden verborgen, obwohl sie technisch korrekt eigentlich als Hardlinks oder Symlinks dargestellt werden müßten. Prinzipiell hat nämlich jede Datei auf FAT bis zu zwei Namen.


yasar
12.5.2013 22:22
Kommentarlink

Nun ich war einer derjenigen, der einen sektor-editor vorgeschagen hat. Natürlich gehe ich davon aus, daß derjenige, der mit einem hexeditor die Bytes in den sektoren zurechtschnitzt, sich das dateisystemformat anschaut udn das passsend so macht, daß dann hinterher die Datenstrukturen wieder passen. 🙂

PS: Die ersten Disketten, die ich mit Hexeditoren gpatcht habe, waren Apple-Disketten im DOS3-Format (nicht mit MSDOS verwechseln), Ich habe damals Loderunner und Space Invaders direkt auf den Disketten gepatcht. War einfacher, als die erst in den Speicher zu laden und dann wieder abzuspeichern. Apple hat damals netterweise vollständig die Dokumentation inklusive ROM-Listeings udn Schaltplänen mitgeliefert.


Hadmut
12.5.2013 22:31
Kommentarlink

Ich hab in den 70er Jahren in den ersten Textsystemen die Willkommensmeldung geändert und etwas später im damaligen Standard-CP/M-Textsystem in Wordstar die Escape-Sequenzen für den Drucker direkt reingepatcht. Machte man damals so.


Stefan
12.5.2013 23:52
Kommentarlink

Auf die Gefahr hin, dass meine Tipps auch völlig deplaziert sein könnten, denn offenbar hast du schon ein ordentliches Bündel Zeit in das Problem reingesteckt, hier meine Erste-Hilfe Maßnahmen bei solchen Dateien:

– Bei samba/vfat gibt es die mount-Option “iocharset”. Den auf utf-8 zu setzen hat schon den ein oder anderen “questionmark-from-hell” von meinen Systemen zu tilgen geholfen.
– Wenn die Option nicht besteht, gibt es immernoch die Möglichkeit eine Datei anhand inode nummer umzubenennen.

$ ls -lai
[…]
6817430 -rw-r–r– 1 user group 43336 May 12 23:48 filename_from_hell.mp4
[…]
$ find . -inum 6817430 -exec mv ‘{}’ fixed.mp4 \;


Hadmut
13.5.2013 0:04
Kommentarlink

@Stefan: Nein, nützt beides nicht. iocharset ändert überhaupt nichts daran, dass ein / eben ein / ist.

und das mit find nutzt auch überhaupt nichts, weil das auch nur (wenn überhaupt) den Dateinamen ausgibt, und damit ein mv nicht möglich ist.

find macht auch nichts anderes, als den Dateinamen anzugeben, und das Ergebnis ist dasselbe, als würde man den Namen direkt hinschreiben.

Du versuchst da, die Shell auszutricksen, und übersiehst dabei, dass das Problem im Betriebssystem und nicht in der Shell liegt.


Stefan
13.5.2013 0:26
Kommentarlink

Wollte ich garnicht alles garnicht bestreiten. 😉 Willst du uns nicht dein Skript zeigen? Sowas könnte ja auch anderen passiern…


Hadmut
13.5.2013 7:45
Kommentarlink

Das Slript ist derzeit weit von einem vorzeigbaren und einsetzbarem Zustand entfernt. Vieles ist noch inkorrekt oder nur für den Fall programmiert, den ich hier habe, teilweise Werte dieses einen Dateisytem hart reincodiert. Das müsste ich erst für den allgemeinen Fall ausprogrammieren und an mehreren Dateisystemen testen, wozu mir momentan die Zeit fehlt. das war erst mal der erste Schuß zum Selbstverstehen.

Außerdem ist die Webseite mit der Beschreibung weitaus nützlicher zum Lesen.


Jens
13.5.2013 0:40
Kommentarlink

“iocharset ändert überhaupt nichts daran, dass ein / eben ein / ist.”

Nun, das hängt vom Charset ab, oder?


Hadmut
13.5.2013 7:48
Kommentarlink

@Jens: Nein. Weil alle mir auswendig bekannten Charsets im unteren Bereich des ASCII mit den Sonderzeichen gleich sind und außerdem nur die Übersetzung gesteuert wird, aber nicht beeinflusst wird, dass im Dateisystem UCS2 verwendet wird. Ein / bleibt ein /.


Knut
13.5.2013 12:31
Kommentarlink

Schöner Artikel.
Die Lösung ist mal wieder ein Beweis dafür, dass automount abschaltbar sein sollte, weil es auch Probleme verursacht. Manchmal ist der Bediener halt doch schlauer als der Rechner und weiß was er tut.

Schön ist die Notation “samba/vfat”. Ich dachte bisher Samba sei die Implementierung des CIFS-Protokolls und nicht das Protokoll selbst.


yasar
13.5.2013 12:36
Kommentarlink

EBCDIC 0x61
ASCII 0x2F

🙂


Klaus
13.5.2013 16:39
Kommentarlink

Verstehe kein Wort. Bin aber froh, hier endlich mal wieder Artikel ohne Gendergedöns zu sehen 🙂


Lohengrin
13.5.2013 19:17
Kommentarlink

Danke für die interessante Information.

Ich hätte wohl erstmal versucht, den Dateinamen mit dd zu überschreiben, und weil das wegen der Prüfsumme zu kompliziert ist, dann den Dateiinhalt mit dd in irgendeine gleich lange Datei zu übertragen.

Ist schon der reine Wahnsinn, dass das Gerät eine Datei mit / im Namen anlegen konnte. Ich habe nicht erwartet, dass es so etwas gibt.