Posts mit dem Label file werden angezeigt. Alle Posts anzeigen
Posts mit dem Label file werden angezeigt. Alle Posts anzeigen

Dienstag, 22. November 2016

Some thoughts about risks in TIFF file format

Introduction

TIFF in general is a very simple fileformat. It starts with a constant header entry, which indicates that the file is a TIFF and how it is encoded (byteorder).
The header contains an offset entry which points to the first image file directory (IFD). Each IFD has a field which counts the number of associated tags, followed by an array of these tags and an offset entry to the next IFD or to zero, which means there is no further IFD.
Each tag in the array is 12 Bytes long. The first 4 bytes indicate the tag itself, the next 2 bytes declare the value-type, followed by 2 bytes counting the values. The last 4 bytes are either an offset or hold the values themselves.

What makes a TIFF robust?

In the TIFF specification, there are some hints which help us to repair broken TIFFs.
The first hint is that all offset-addresses must be even. The second important rule is that the tags in an IFD must be sorted in an ascending order.
At last, the TIFF spec defines different areas in the tag range. This is to guarantee that the important values are well defined.
If we guarantee that a valid TIFF was stored, there is a good chance to detect and repair broken TIFFs using these three hints.

What are the caveats of TIFF?

As a proof of concept there is also a tool "checkit_tiff_risks" provided in this repository. Using this tool, users can analyze the layout of any baseline TIF file.
The most risky memory ranges are the offsets. If a bitflip occurs there, the user must search the complete 4GB range. In practise, the TIF files are smaller, and so this size is the searchspace for offsets.
The most risky offsets are the ones which are indirect offsets. This means the IFD0 offset and the StripOffset tag (code 273).
Here an example of a possible complex StripOffset encoding:
The problem in this example is that TIFF has no way to find out how many bytes are part of the pixel-data stream. The existing StripByteCounts tag only stores the expected pixel data length after decompression.
This makes the StripOffset tag very fragile. If a bitflip changes the offset of the StripOffset tag, the whole pixel information might be lost.
Also, if a bitflip occurs in the offset area that the StripOffset tag points to, the partial pixel data of the affected stripe is lost.
If compression is used, the risk of losing the whole picture is even higher, because the compression methods do not use an end-symbol. Instead, the buffer sizes as stored in the StripByteCount tag are used. Therefore, a bit-error in the Compression tag, the StripOffset tag, the StripByteCount tag or in the memory-map where StripOffset points to, could destroy the picture information.

Upcoming next…


In upcoming versions of checkit_tiff, we would provide a tool to analyze the distribution of risky offsets in given TIFF-files.  This will objectify the discussion about robust fileformats vs. compression.

Here a short preview:

$>  ./checkit_tiff_risk ../tiffs_should_pass/minimal_valid.tiff

This reports this kind of statistics:

[00], type=                  unused/unknown, bytes=         0, ratio=0.00000
[01], type=                        constant, bytes=         4, ratio=0.01238
[02], type=                             ifd, bytes=       130, ratio=0.40248
[03], type=                  offset_to_ifd0, bytes=         4, ratio=0.01238
[04], type=                   offset_to_ifd, bytes=         4, ratio=0.01238
[05], type= ifd_embedded_standardized_value, bytes=        52, ratio=0.16099
[06], type=   ifd_embedded_registered_value, bytes=         0, ratio=0.00000
[07], type=      ifd_embedded_private_value, bytes=         0, ratio=0.00000
[08], type=ifd_offset_to_standardized_value, bytes=        12, ratio=0.03715
[09], type=  ifd_offset_to_registered_value, bytes=         0, ratio=0.00000
[10], type=     ifd_offset_to_private_value, bytes=         0, ratio=0.00000
[11], type=      ifd_offset_to_stripoffsets, bytes=         0, ratio=0.00000
[12], type=               stripoffset_value, bytes=        30, ratio=0.09288
[13], type=              standardized_value, bytes=        87, ratio=0.26935
[14], type=                registered_value, bytes=         0, ratio=0.00000
[15], type=                   private_value, bytes=         0, ratio=0.00000
counted: 323 bytes, size: 323 bytes


In this example the StripOffset is encoded directly (there are only one stripe). The problematic bytes are the offset-addresses (affected 20 Bytes of 323 Bytes).

In opposite to this example, here a special file using multiple strips:

$>  ./checkit_tiff_risk ../tiffs_should_pass/minimal_valid_multiple_stripoffsets.tiff

This reports this kind of statistics:

[00], type=                  unused/unknown, bytes=         0, ratio=0.00000
[01], type=                        constant, bytes=         4, ratio=0.01250
[02], type=                             ifd, bytes=       122, ratio=0.38125
[03], type=                  offset_to_ifd0, bytes=         4, ratio=0.01250
[04], type=                   offset_to_ifd, bytes=         4, ratio=0.01250
[05], type= ifd_embedded_standardized_value, bytes=        44, ratio=0.13750
[06], type=   ifd_embedded_registered_value, bytes=         0, ratio=0.00000
[07], type=      ifd_embedded_private_value, bytes=         0, ratio=0.00000
[08], type=ifd_offset_to_standardized_value, bytes=        16, ratio=0.05000
[09], type=  ifd_offset_to_registered_value, bytes=         0, ratio=0.00000
[10], type=     ifd_offset_to_private_value, bytes=         0, ratio=0.00000
[11], type=      ifd_offset_to_stripoffsets, bytes=        40, ratio=0.12500
[12], type=               stripoffset_value, bytes=        30, ratio=0.09375
[13], type=              standardized_value, bytes=        56, ratio=0.17500
[14], type=                registered_value, bytes=         0, ratio=0.00000
[15], type=                   private_value, bytes=         0, ratio=0.00000
counted: 320 bytes, size: 320 bytes


Here you can see we have the type 11, which points StripOffset to an array of offset adresses, where the pixel data could be found. This is similar to the diagram above. In this case we have 40 bytes with high bitflipping risk.




Montag, 1. September 2014

Dateiformat-Explosion

Vorwort


Zum Bibliothekartag 2014 gab es im Nachtrag zu unserem Vortragsblock eine rege Diskussion darüber, ob es sinnvoll ist, erst einmal alle Dateien in ein Langzeitarchiv aufzunehmen und sich später Gedanken darüber zu machen, wie man deren Langzeitverfügbarkeit z. B. durch Formatmigration sicherstellt.

Ich habe viel recherchiert, aber wenig belastbare Zahlen gefunden. Ein guter Indikator könnten die Signaturen für DROID sein, einem Programm zur Ermittlung des Datenformates unbekannter Dateien.

Auf Pronom findet man 77 Versionen von 08-2005 bis 07-2014, von ehedem 766 bis heute 1.459 von DROID unterstützten Signaturen.

Alternativ könnte man die Datenbanken des Unix-Befehls file (http://www.darwinsys.com/file/) heranziehen. Auf ftp://ftp.astron.com/pub/file/ findet man Versionen von 08-2008 bis 06-2014.

Eine andere Möglichkeit wäre, die bei der IANA registrierten MIME-Types (zur Zeit 1.087 verschiedene Signaturen) heranzuziehen: http://www.iana.org/assignments/media-types/media-types.xhtml

Außerdem wurde ich noch auf http://fileformats.archiveteam.org/wiki/Main_Page aufmerksam gemacht.

Leider gibt es zu den letzten beiden Links keine auswertbare Timeline.

Was uns 'file' verrät


Wir können annehmen, dass sowohl bei 'file' als auch bei DROID, die Signaturen in gewisser Weise die Dateiformate enthalten, die auch weit verbreitet sind. Wie stark wissen wir nicht, aber wenn sie total unbedeutend wären, hätte sich keiner die Mühe gemacht, für diese Signaturen zu erstellen.

Wir können ferner annehmen, dass nach einer gewissen Einschwingzeit die Zahl der Dateiformate in diesen Signatur-Datenbanken der Anzahl der tatsächlich verbreiteten Dateiformate entspricht.

Im Folgenden das Diagramm der Entwicklung der Dateiformate basierend auf der file Magic Bytes Datenbank von Ende 1999 bis Mitte 2014.

Count of different fileformats based on magic bytes database of file, 08/2014, Andreas Romeyke

Als Grundlage für das Diagramm diente das GIT-Repository von file, genauer das Verzeichnis 'magic/Magdir'. Über die Tags extrahierte ich alle Versionen des Verzeichnisses und ließ das folgende Script die dem Diagramm zugrunde liegende CSV-Datei erstellen:


#!/bin/bash
echo "#version, date, group, count"
for version in *;do
  for sub in $version/magic/Magdir/* ; do
    group=$(basename $sub); 
    count=$(cat $sub | grep "^0" | wc -l);
    date=$(cd $version; git log --name-status --date=raw \
    --pretty='format:commit,%ae,%ai,%ce'| cut -d "," -f 3| head -n 1|\
    cut -d " " -f 1)
    echo "$version, $date, $group, $count"; 
  done ;
done


Sehr überraschend für uns war der nahezu lineare Verlauf der Formatvielfalt. Um auszuschließen, dass dies andere Ursachen hat, war es notwendig, eine zweite, von file unabhängige Quelle heranzuziehen.

DROID/Pronom als Quelle


Die Entwickler von DROID haben, wie oben bereits angedeutet, ihre Signaturfiles von Version 1 bis zur aktuellen Version 77 online angeboten.

Pronom hat als Projekt das Ziel, für Langzeitarchive eine verlässliche Quelle für Dateiformate, Signaturen und zugeordnete Programme bereitzustellen. DROID übernimmt als Programm die Aufgabe der Identifikation unbekannter Formate.

Durch den strukturierten Aufbau der Signaturdateien ist eine automatische Auswertung möglich.

Im Folgenden ist die Anzahl der verschiedenen Dateiformate über die Zeit zu sehen:


Count of different fileformats based on DROID signatures, 08/2014, Andreas Romeyke
Auch hier ergibt sich ein nahezu linearer Zuwachs, spätestens ab Mitte 2010.

Da erst in späteren Signaturen ab ca. 2009 auch MIME-Types hinterlegt wurden, sieht die Entwicklung der einzelnen Kategorien im folgenden Diagramm etwas anders aus:

Count of different fileformats per category, based on DROID signatures, 08/2014, Andreas Romeyke

Hier noch das Shell-Script:

#!/bin/bash
echo "#version, date, application, text, images, audio, video"
for i in droid/DROID_SignatureFile_V*.xml; do
  version=$(echo $i |sed -e "s/.*_V\([^.]\+\).*/\1/" );
  date=$(cat $i | grep -m 1 "DateCreated=" |\
  sed -e "s/.*DateCreated=\"\(....-..-..\).*/\1/" );
  echo -n "$version, $date,"
  for category in application text image audio video; do
    count=$(xmlstarlet sel -t -v\
    "//_:FileFormat[contains(@MIMEType, \"$category/\")]/@Name" $i| \
    sort | uniq | wc -l)
    echo -n "$count,"
  done;
  echo
done | sort -n 

Wenn pro Jahr also 90 neue Dateiformate…


Die Anzahl der  Dateiformate wächst nahezu linear. Pro Jahr kommen im Schnitt 90 neue Dateiformate hinzu, die immerhin eine solche Verbreitung erfahren, dass sie in den Signatur-Datenbanken Widerhall finden.

Wenn wir in Zeiträumen von 50 Jahren denken, haben wir knapp 5000 Formate zu behandeln.

Wer alles in sein Langzeitarchiv hineinkippt, was ihm seine Nutzer anbieten, wird daher in Zukunft kaum die Ressourcen haben, Spreu und Weizen zu trennen und eine Langzeitverfügbarkeit sicherzustellen.

Die Entwicklung zeigt, dass dringend eine Spezialisierung der Langzeitarchive notwendig ist, um Wissen über Formate aufzubauen und sich gegenseitig zu unterstützen.

Es zeigt auch weiterhin, dass Nutzer der Archive über die Problematik aufgeklärt werden müssen.