Fallstrick zu gzip - gut zu wissen

reinhard@finalmedia.de Sat 29 Apr 2023 07:15:46 PM CEST

Ein "Hash wider Erwarten" oder der "useful use of cat"

Das Kompressionstools gzip verhält sich anders, je nachdem ob es via einen Eingabestrom via Dateiumleitung "<" erhalten halt, oder über "|".

Im Fall der Dateiumleitung "<" in stdin arbeitet es wider Erwarten dennoch in einem für den Inhalt nicht-deterministischen Modus, der den Zeitstempel des Streams mitspeichert und somit für einen veränderten Hashwert des Ergebnisses sorgt, auch wenn sich der Inhalt des Eingabestroms selbst nicht verändert hat.

Normalerweise würde dies gzip nur tun, wenn gzip test.txt direkt im Dateimodus aufgerufen wurde. Dieser Fall steht ja auch im Manual: " By default, gzip keeps the original file name and timestamp in the compressed file.". Bei der Dateiumleitung via "<" wird zwar kein Dateiname mehr, aber immer noch der Zeitstempel gespeichert. Letzteres ist unerwartet.

TL;DR. Lösung: Durch Mitgabe des Parameters -n kann das wiederum verhindert werden. Auch bei dem Einsatz von alternativen tools wie xz oder bzip2 tritt das Problem nicht auf. Dort sind alle drei Variante gleich, ohne dass ein -n notwendig wäre.

Vorbereitung für den Test


$ echo hallo > test.txt

Variante A


$ gzip < test.txt > test.gz
$ sha256sum test.gz
bd963fbd781bc67ff092a861ff7e41bfa714aa51aa1f9f47713955e0c37173b5  test.gz
$ gzip < test.txt > test.gz
$ sha256sum test.gz
bd963fbd781bc67ff092a861ff7e41bfa714aa51aa1f9f47713955e0c37173b5  test.gz
$ gzip < test.txt > test.gz
$ sha256sum test.gz
bd963fbd781bc67ff092a861ff7e41bfa714aa51aa1f9f47713955e0c37173b5  test.gz
$ touch test.txt
$ gzip < test.txt > test.gz
$ sha256sum test.gz
ead0600b4d5456162b76c93f237bdda880daed5e04157b8f3d4013426826cd28  test.gz
$ # ^ DIESER HASH IST UNERWARTET, DA SICH AM INHALT VON 
$ # test.txt NICHTS GEÄNDERT HAT.

Variante B

Hier zum Vergleich die Alternative mit cat in gzip


$ cat test.txt | gzip > test.gz
$ sha256sum test.gz
814bbc36b86a85823993312c750c29952f5857fabdc3f2eea4738dc073206b7b  test.gz
$ cat test.txt | gzip > test.gz
$ sha256sum test.gz
814bbc36b86a85823993312c750c29952f5857fabdc3f2eea4738dc073206b7b  test.gz
$ touch test.txt
$ sha256sum test.gz
814bbc36b86a85823993312c750c29952f5857fabdc3f2eea4738dc073206b7b  test.gz

Variante C

und zum Vergleich die Variante mit gzip -n


$ gzip -n < test.txt > test.gz
$ sha256sum test.gz
814bbc36b86a85823993312c750c29952f5857fabdc3f2eea4738dc073206b7b  test.gz
$ gzip -n < test.txt > test.gz
$ sha256sum test.gz
814bbc36b86a85823993312c750c29952f5857fabdc3f2eea4738dc073206b7b  test.gz
$ touch test.txt
$ gzip -n < test.txt > test.gz
$ sha256sum test.gz
814bbc36b86a85823993312c750c29952f5857fabdc3f2eea4738dc073206b7b  test.gz

Analyse

Die Bytes 5-8 der Datei test.gz unterscheiden sich dabei


$ hd test.gz
00000000  1f 8b 08 00 XX XX XX XX  00 03 cb 48 cc c9 c9 e7  |.....?Md...H....|
00000010  02 00 e0 96 ba c3 06 00  00 00                    |..........|
0000001a

Nur im Fall mit -n und mit | dann stets 00 00 00 00. Details sind auch via gzip -lv test.gz ersichtlich, die crc unterscheided sich jedoch nicht, da diese sich auf die unveränderte payload bezieht. Die Veränderung findet nur im header von gzip statt.

Warum und weshalb ist das überhaupt wichtig?

Bei Backups. Auch wenn sich der Inhalt nicht geändert hat, ergibt sich damit eine andere Datei. Damit ist ist (je nach Sicherungssystem das z.B. auf Dateiebene und nicht auf Blockebene arbeitet) - somit eine inkrementelle Sicherungsmethode nicht mehr effizient, da aufgrund dieser einzelnen Änderung in der Datei unnötige Kopien angelegt werden.

Ein anderer Punkt ist der Datenschutz. Es kann ggf. explizit gewünscht sein, dass der exakte Entstehungszeitpunkt des Datenstroms nicht im Archiv protokolliert wird.