Freitag, 19. August 2016

Image File Directories reparieren

(english version below)

Vor Kurzem hatten mein Kollege Andreas und ich eine Diskussion darüber, wie man das defekte Image File Directory (IFD) einer TIFF-Datei reparieren könnte. Er hatte dazu eine Änderung in fixit_tiff eingebaut, die ein neues und korrigiertes IFD an das Ende der TIFF-Datei schreibt und das IFD-Offset einfach auf das neue IFD zeigen lässt, so wie es auch in der libtiff vorgesehen ist. Das ursprüngliche (defekte) IFD, das üblicherweise irgendwo am Anfang der Datei steht, wird dabei nicht verändert und liegt auch in der neuen Datei wieder vor. Der einzige Unterschied ist, dass jetzt das IFD-Offset nicht mehr auf das alte IFD zeigt und es damit von TIFF-Readern nicht mehr ausgelesen wird. Die Datei wächst also mit jeder Änderung am IFD an und es bleibt immer mehr "Müll" in der Datei zurück. Andererseits hat man aber auch eine Art Historie, weil alle bisherigen IFD-Versionen erhalten bleiben.

Für mich fühlte sich diese Methode unsauber an, weil ich den Meinung bin, dass Dateien keinen unreferenzierten Datenmüll enthalten sollten; insbesondere im Kontext der Langzeitarchivierung. Nicht nur ist es eine Verschwendung von Speicher, sondern späteren Datenarchäologen könnten daraus auch Probleme erwachsen, wenn sie versuchen, die Dateien zu interpretieren und dabei unreferenzierte Datenblöcke vorfinden.

Überraschenderweise (zumindest für mich) ist das Vorgehen aber völlig konform mit der Spezifikation und hat darüber hinaus noch weitere Vorteile.
  1. Die Änderung ist schnell und billig. An die bestehende Datei müssen nur ein paar Kilobytes angefügt und der IFD-Offset im Dateiheader korrigiert werden.
  2. Wie erwähnt bleibt die "Historie" erhalten.
  3. Alle anderen Offsets, die in der Datei verwendet werden, können unverändert bestehen bleiben. Dadurch wird das Verfahren sehr robust und fehlerunanfällig.
Inzwischen ist nun die Implementierung geändert worden. Da wir meist nur TIFF-Tags ändern oder löschen ist es unwahrscheinlich, dass das IFD sich vergrößert. Daher wird nun das IFD an Ort und Stelle verändert. Nun entsteht zwar zwischen dem IFD und den Bild-/Nutzdaten ein Leerraum, in dem potentiell Datenmüll steht, aber auch das wäre laut TIFF-Spezifikation erlaubt. Außerdem ist der Leerraum bedeutend kleiner als der große Block des ursprünglichen IFDs.

Es gibt aber noch eine dritte Möglichkeit, das Problem zu lösen. Dabei würde man die TIFF-Datei komplett neu schreiben, so dass keine Lücken zurückbleiben würden. Nach meinem Dafürhalten ist das die sauberste Option. Sie hat allerdings handfeste Nachteile.
  1. Der Entwicklungsaufwand ist hoch. Um die ganze Datei zu lesen, die einzelnen Bestandteile sicher zu verwalten und zu ändern, muss einiges an Programmcode geschrieben werden.
  2. Alle Offsets müssen geändert werden. Dieser Prozess ist fehleranfällig und bewegen die Datei weiter vom Original weg. Außerdem müssen auch Offsets innerhalb von privaten Tags geändert werden. Da dort aber die innere Struktur oft unbekannt ist, kann nicht sichergestellt werden, dass alle Offsets nach dem Schreiben noch korrekt sind.
  3. Die Datei muss neu geschrieben werden. Dabei steigt die Wahrscheinlichkeit, dass Bitfehler auftreten.
  4. Die Daten können nicht mehr verarbeitet werden, ohne die komplette Datei in den Speicher zu laden.
  5. Durch den hohen Aufwand wird ein höherer Datendurchsatz benötigt, die Hardware wird stärker belastet und es entsteht mehr Rechenzeit. Bei großen Mengen an zu korrigierenden Dateien kann sich dieser Mehraufwand deutlich bemerkbar machen.
Mich würde vor allem interessieren, ob es in der Community dazu schon Meinungen oder Best Practices gibt, und wie diese lauten. Welche Variante wird bevorzugt? Ein neues IFD anhängen, das bestehende IFD an Ort und Stelle ändern, oder die Datei komplett neu schreiben?
Ich selbst bin immer noch hin und her gerissen zwischen meinem Qualitätsanspruch einerseits und den hohen Kosten dafür andererseits.



english version

My colleagure Andreas and I recently had a discussion about the best way to fix the broken Image File Directory (IFD) of a TIF file. He had implemented a change in fixit_tiff to write a new corrected IFD to the end of the TIF-file and correct the IFD offset, just the way it is stipulatey by the libtiff. The original (defective) IFD, that is usually placed somewhere in the beginning of the file, is not changed at all and can be found in the new file as well. The key difference is that the IFD offset doesn't point to the old IFD anymore, making TIFF readers ignore it. Hence, the file grows with every change in the IFD and more and more "garbage" is kept in the file. On the other hand, a kind of version history is created inside of the TIFF itself, because all former IFD versions are kept.

For me, this method seemed unclean, because I think that files should not contain any unreferenced garbage date; especially so in the context of digital preservation. It's not only a waste of storage, but might lead to problems if data archaeologists one day try to interpret the data and find unreferenced blocks.

Most surprisingly (at least for me), this method completely complies with the TIFF specification and also brings some further advantages:
  1. It's a fast and cheap change. Only a few kilobytes need to be added to the file, and the IFD offset needs to be corrected.
  2. As mentioned before, the "history" stays intact.
  3. All other offsets used in the file can remain the same as before, which makes this method quite robust and sturdy.
In the meantime, the implementation has been changed. Given that we usually just change or delete TIFF tags, it's improbable that the IFD grows in size. Hence, now the IFD is changed in-place. Now there's a little free space between the IFD and the payload data, but that's supported by the TIFF specification as well. Moreover, the space is much smaller than the large block of the original IFD that remained when using the first method.

There is a third method to solve the problem. A TIFF writer could completely rewrite the entire file, leaving no free spaces whatsoever. In my opinion, this is the most elegant option. It does, however, have some serious disadvantages.

  1. The development would be quite an effort. A lot of code needs to be written to read, manage and alter all componentes of the file.
  2. All offsets need to be altered. This process is error prone and brings the files further from their original state. Furthermore, offsets inside of private tags need to be changed as well. However, as their inner structure is often unknown, no-one can make sure that all offsets are still correct after writing the file.
  3. The entire file needs be be rewritten. During this process, bit flip errors might occur.
  4. The files cannot be processed without loading the whole file into the RAM.
  5. Larger I/O capacities are needed, the hardware is stressed more and more CPU cycles are burnt. If there are larger amounts of files the need correction, this effort will surely be noticeable.
I would be most interested if the community already has opinions or best practices on this matter, and what these are. Which option do you prefer? Attaching a new IFD, altering the existing IFD in-place, or re-writing the whole file?
I myself am torn between my personal demand for high quality standards on the one hand and the high costs to reach them on the other hand.