Fragen? Antworten! Siehe auch: Alternativlos
Wer ein diff und patch für Binärdateien sucht, für den gibt es leider kein Standardtool. Es gibt da diverse Tools auf dem Markt, das bekannteste ist xdelta, aber es gibt auch bsdiff, BDelta, bdiff (lag mal auf Sourceforge herum, aber scheint verloren gegangen zu sein; ich hatte hier noch einen Tarball für Version 1.0.4 herumliegen), jptch/jdiff und sicher noch mehr, die ich nicht kenne.
Mein Anwendungsfall war, dass ich hier ein ISO-Image einer Linux-Distribution ändere, und gerne davon wegkommen wollte, immer das ganze ISO übertragen zu müssen. Und so ein ISO, das sind mal eben 4 GB oder so.
Bei solchen Datenmengen trennt sich die Spreu vom Weizen. Ich nehme im Wesentlichen das unveränderte ISO-Image und füge ein paar Dateien dazu. Meine Erwartungshaltung war: Der Patch sollte 1 MB nicht überschreiten.
xdelta rödert länglich herum und erzeugt einen mehrere hundert MB großen Patch (ich nehme das ISO und füge ein paar Dateien dazu, wir reden hier von sowas wie 100 KB zusätzlichen Dateien). Da brach ich ab.
bsdiff versucht, die ganze Datei (mit read statt mmap!) in den Speicher zu lutschen und failed dabei, weil read() von 4 GB unter Linux nur 2 GB liefert. Ich weiß nicht, welcher Vollpfosten das bei Linux entschieden hat. In meinen Augen ist das ein Bug. War wahrscheinlich ein gut gemeintes Security-Theater oder so. bsdiff ist also auch ausgefallen.
jojodiff rödelt anderthalb Minuten und erzeugt dann 1,3 MB. Geht doch!
bdelta braucht 10 Sekunden (!?) und erzeugt einen Patch von 1,6 MB. Der funktioniert dann leider nicht. Wie sich rausstellt, ist das Dateiformat von bdelta hirnrissig und failed nicht wie im README dokumentiert ab 4 GB sondern schon vorher. Für das Abspeichern der relativen Offsets zwischen Quellen für Patches nimmt der 32-bit signed Integer, und prüft natürlich auch nicht, ob der Wert, den er abspeichern will, auch passt. bdelta ist aber ansonsten ausgesprochen sympathisch, weil der Quellcode so winzig und unprätentiös ist, und das Format so trivial. Also habe ich mal einen Patch gemacht, damit er die Integer mit variabler Länge abspeichert (ich nehme dafür das Verfahren von den Google Protocol Buffers, das ist ganz schlau wie ich finde). Das ganze Ding platzt wahrscheinlich immer noch bei Dateien über 4 GB, aber für meinen konkreten Anwendungsfall tut es ganz gut.
Weil das Projekt soweit verlassen wirkt, tu ich den Patch mal auf meinen Webserver, falls das jemand braucht.
Update: Ein langjähriger Leser weist darauf hin, dass ich die Gründe für das 2 GB-Limit schon mal erklärt hatte. Mein Kumpel Erdgeist prüft das übrigens gerade unter BSD, und zumindest OS X hat auch ein 2 GB-Limit, sogar dokumentiert bei readv.
Update: Unter den freien BSDs hat read() auch ein 2GB-Limit. WTF!?
Update: Einige Leser haben herausgefunden, dass man unter FreeBSD das 2GB-Limit mit den sysctls debug.devfs_iosize_max_clamp und debug.iosize_max_clamp abschalten kann.