Diff de deux gros fichiers binaires bruts similaires

Disons que jai un fichier de 4 Go abc sur mon ordinateur local. Je lai téléchargé sur un serveur distant via SFTP, cela a pris quelques heures.

Maintenant, jai légèrement modifié le fichier (probablement 50 Mo maximum, mais pas doctets consécutifs dans ce fichier) localement, et lai enregistré dans abc2. Jai également conservé le fichier dorigine abc sur mon ordinateur local.

Comment calculer un diff binaire de abc et abc2?

Applications:

  • Je ne pouvais envoyer quun fichier patch (probablement 100 Mo maximum) à le serveur distant, au lieu de télécharger à nouveau le fichier abc2 (cela prendrait encore quelques heures!), et recréez abc2 sur le serveur distant serveur de abc et patch uniquement.

  • Localement, au lieu de gaspiller 8 Go pour sauvegarder à la fois abc et abc2, je nai pu enregistrer que abc + patch, donc cela prendrait < 4100 Mo seulement.

Comment faire?

PS: pour le texte, je sais diff, mais ici je « cherche quelque chose qui pourrait fonctionner pour nimporte quel format binaire brut, ce pourrait être des fichiers zip ou des exécutables ou même dautres types de fichiers.

PS2: Si possible, je ne veux pas utiliser rsync; Je sais quil peut répliquer les changements entre 2 ordinateurs de manière efficace (sans renvoyer des données qui nont pas changé), mais ici je veux vraiment avoir un fichier patch, reproductible plus tard si Jai à la fois abc et patch.

Réponse

Pour la deuxième application / problème, jutiliserais un programme de sauvegarde de déduplication comme restic ou borgbackup, plutôt que dessayer pour suivre manuellement les « patches » ou les diffs. Le programme de sauvegarde restic vous permet de sauvegarder des répertoires de plusieurs machines vers le même référentiel de sauvegarde, en dédupliquant les données de sauvegarde à la fois parmi les fragments de fichiers dune machine individuelle ainsi quentre les machines. (Je nai aucune expérience utilisateur avec borgbackup, donc je ne peux rien dire à propos de ce programme.)

Calcul et stockage dun diff de abc et abc2 peuvent être créés avec rsync.

Ceci est un exemple avec abc et abc2 étant de 153 Mo. Le fichier abc2 a été modifié en écrasant le premiers 2,3 Mo du fichier avec dautres données:

 $ ls -lh total 626208 -rw-r--r-- 1 kk wheel 153M Feb 3 16:55 abc -rw-r--r-- 1 kk wheel 153M Feb 3 17:02 abc2  

Nous créons patch pour transformer abc en abc2 et lappeler abc-diff:

 $ rsync --only-write-batch=abc-diff abc2 abc  
 $ ls -lh total 631026 -rw-r--r-- 1 kk wheel 153M Feb 3 16:55 abc -rw------- 1 kk wheel 2.3M Feb 3 17:03 abc-diff -rwx------ 1 kk wheel 38B Feb 3 17:03 abc-diff.sh -rw-r--r-- 1 kk wheel 153M Feb 3 17:02 abc2  

Le fichier généré abc-diff est le diff réel (votre « fichier de patch »), tandis que abc-diff.sh estun court script shell que rsync crée pour vous:

 $ cat abc-diff.sh rsync --read-batch=abc-diff ${1:-abc}  

Ce script modifie abc pour quil devienne identique à abc2, étant donné le fichier abc-diff:

 $ md5sum abc abc2 be00efe0a7a7d3b793e70e466cbc53c6 abc 3decbde2d3a87f3d954ccee9d60f249b abc2 $ sh abc-diff.sh $ md5sum abc abc2 3decbde2d3a87f3d954ccee9d60f249b abc 3decbde2d3a87f3d954ccee9d60f249b abc2  

Le fichier abc-diff pourrait maintenant être transféré partout où vous avez abc. Avec la commande rsync --read-batch=abc-diff abc, vous appliqueriez le correctif au fichier abc, en transformant son contenu pour quil soit le même que celui de abc2 sur le système sur lequel vous avez créé la différence.

Réappliquer le patch une deuxième fois semble sûr. Il ny a pas de message derreur et le contenu du fichier ne change pas (la somme de contrôle MD5 ne change pas).

Notez quà moins que vous ne créiez un « patch inversé » explicite, il ny a aucun moyen dannuler facilement lapplication du patch.


Jai également testé lécriture de la modification de 2,3 Mo à un autre endroit dans les données abc2, un peu plus loin (à environ 50 Mo), ainsi quau début. Le « patch » généré faisait 4,6 Mo de large, ce qui suggère que seuls les bits modifiés ont été stockés dans le patch.

Commentaires

  • Merci beaucoup @Kusalananda, ‘ est super! PS: rsync --read-batch=abc-diff ${1:-abc} (script .sh généré automatiquement) a donné remote destination is not allowed with --read-batch rsync error: syntax or usage error (code 1) at main.c(1326) [Receiver=3.1.2], mais rsync --read-batch=abc-diff abc a fonctionné avec succès.Quelle est la différence entre ces deux commandes similaires?
  • 2/2 Existe-t-il un moyen de prendre abc en entrée, appliquer le correctif diff-abc avec --read-batch mais sans modifier abc  » sur place « , mais plutôt une sortie dans un nouveau fichier abc3? (si possible tout avec rsync, sans tuyauterie, pour que cela fonctionne facilement sous Linux ainsi que Windows qui a également rsync.exe disponible)
  • @Basj Les commandes feraient des choses différentes si $1 avait une valeur. ${1:-abc} signifie que  » utiliser le premier paramètre de position ($1) sauf sil ‘ est vide ou indéfini. Dans le cas où il ‘ est vide ou non défini, utilisez abc à la place « . Je ‘ m en supposant que $1 avait une valeur lorsque vous lavez essayé, peut-être quelque chose quil a interprété comme un adresse de destination distante.
  • @Basj Je ‘ Je ne suis pas tout à fait sûr que cela soit possible, mais je ‘ ll jetez un œil demain après votre sommeil.
  • Merci pour votre réponse sur ${1:-abc}. Cela a probablement échoué parce que je lai essayé sous Windows (je ‘ m en utilisant rsync à la fois sur Linux pour mon serveur distant et Windows localement). Mais cela ‘ est parfait puisque rsync --read-batch=abc-diff abc fonctionne 🙂

Réponse

Comment calculer un diff binaire de abc et abc2?

En utilisant bsdiff / bspatch ou xdelta et autres.

$ bsdiff older newer patch.bin # patch.bin is created [...] $ bspatch older newer patch.bin # newer is created 

Cependant, ces avertissements des pages de manuel sont à noter:

  • bsdiff utilise une mémoire égale à 17 fois la taille de oldfile et nécessite une taille de jeu de travail minimale absolue égale à 8 fois la taille de oldfile .
  • bspatch utilise une mémoire égale à la taille de oldfile plus la taille de newfile , mais peut tolérer un très petit ensemble de travail sans perte dramatique de performances.

Commentaires

  • Pourriez-vous montrer un exemple?
  • Merci pour votre réponse. bsdiff uses memory equal to 17 times the size of oldfile donc cela ne fonctionnera ‘ que pour des fichiers de 4 Go (au moins sur ma machine de 8 Go de RAM).
  • @Basj Ce qui est possible est de découper le fichier de 4 Go en fichiers plus petits (disons 128 Mo chacun), et de faire des deltas individuels. Cela pourrait être enveloppé dans un script. chopped-bsdiff: hachez les fichiers, faites des bsdiff par paires, mettez-les dans une archive. chopped-bspatch: lire les correctifs par paires de larchive, appliquer à des morceaux de fichier dentrée, générer la sortie.
  • @Kaz je vois, mais je ‘ m recherche plus un outil prêt à lemploi qui peut être appelé en 1 ligne (mydiff abc abc2 > patchfile et mypatch abc patchfile > abc3) quelle que soit la taille. De plus, si je découpe en morceaux de 128 Mo, que se passe-t-il si le premier 1 Go de abc == le dernier 1 Go (de fin) de abc2 ? Lorsque nous ‘ comparerons abc-first128mb avec abc2-first128mb, aucune correspondance ne sera trouvée, donc il pourrait ne pas être efficace?

Réponse

Avez-vous essayé de forcer simplement diff pour traiter les fichiers comme du texte:

diff -ua abc abc2 

Comme expliqué ici .

  • -u affiche NUM (par défaut 3) lignes de contexte unifié
  • -a traiter tous les fichiers comme du texte

Cela devrait vous apporter un correctif. Linconvénient de ceci est que les « lignes » pourraient être assez longues et cela pourrait gonfler le patch.

Commentaires

  • Oups, oui, vous navez pas ‘ t veulent réellement le n. Je ‘ suis intéressé de savoir si cela fonctionne comme je ‘ ne sais pas combien de temps le  » lignes  » le seront.
  • Merci pour votre commentaire! Jai créé deux fichiers de 256 Mo très similaires abc et abc2. Ensuite, jai essayé diff -ua abc abc2 > patch, puis jai copié abc vers abc3 et jai essayé de récupérer abc2 grâce à abc3 et patch: patch abc3 < patch, mais cela na pas fonctionné: à la fin, abc3 ne faisait que 1 Ko au lieu de 256 Mo. Une idée?
  • Hmmm, je ne sais pas ce qui sest passé. Je lai juste fait sur ma machine et cela a mieux fonctionné que ce à quoi je mattendais.Jai pris un fichier 382M qui était des entiers aléatoires écrits en binaire dans un fichier. Jai changé 3 octets et jai fait le diff et le patch et cela a fonctionné. Les fichiers résultants étaient égaux à md5sum.
  • Si un gros fichier na pas doctet 0x0a, cest-à-dire une nouvelle ligne, ou très peu, je suppose que cela ne serait pas ‘ t fonctionne si bien, il serait intéressant de tester.
  • Oh bien sûr. Vous pouvez faire une estimation éclairée sur un binaire avec wc -l qui recherchera des sauts de ligne et, daprès mon expérience, sexécute très rapidement. Je mattendrais à ce que sur un binaire arbitraire, cela fonctionne plutôt bien. Par exemple, sur ma machine, jai trouvé un mp4 de 252 millions de pixels contenant 1,2 million de  » lignes  » et un 59M .deb qui avait environ 230 Ko, donc une moyenne de  » lignes  » de moins de 220 octets et 258 octets respectivement. Je ne ‘ pas voir pourquoi ces fichiers seraient si différents des autres, mais vous pourriez certainement être malchanceux. En pratique, je soupçonne que cela fonctionnerait plutôt bien et sinon, ‘ reste un hack amusant.

Réponse

Utilisez xdelta , il a été créé exactement pour ce type dutilisations. Basé sur VCDIFF (RFC 3284) dans les dernières versions.

Commentaires

  • Le lien ne fonctionne pas (y a-t-il une autre URL?). Pourriez-vous également ajouter un exemple en quelques lignes pour montrer comment: 1) calculer le fichier diff patch, et 2) restaurer abc2 , donné uniquement abc et patch?
  • Désolé, URL fixe
  • Merci @vonbrand . Auriez-vous un tel exemple?

Réponse

Compléments à dautres réponses selon mes tests:

Avec diff

Jai créé deux fichiers de 256 Mo très similaires abc et abc2. Ensuite, créons le fichier diff:

diff -ua abc abc2 > abc-abc2.diff 

Essayons maintenant de récupérer abc2 grâce au fichier dorigine abc et abc-abc2.diff:

cp abc abc3 patch abc3 < abc-abc2.diff 

ou

cp abc abc3 patch abc3 -i abc-abc2.diff 

ou

patch abc -i abc-abc2.diff -o abc3 

Cela fonctionne sous Linux. Jai également essayé sur Windows (patch.exe et diff.exe sont également disponibles), mais pour une raison inconnue, il a échoué: le fichier abc3 produit ne fait que 1 Ko au lieu de 256 Mo (I  » Je mettrai à jour cette réponse plus tard ici).

Avec rsync

Comme détaillé dans la réponse acceptée, cela fonctionne:

rsync --only-write-batch=abc-abc2-diff abc2 abc cp abc abc3 rsync --read-batch=abc-abc2-diff abc3 

Avec rdiff

Comme détaillé dans ceci réponse , cest aussi une solution:

rdiff signature abc abc-signature rdiff delta abc-signature abc2 abc-abc2-delta rdiff patch abc abc-abc2-delta abc3 

Testé également sous Windows avec rdiff.exe de ici et ça marche.

Commentaires

  • Je ‘ je suppose que le correctif a échoué sous Windows car il lisait le fichier dentrée en mode  » text  » qui signale la fin du fichier lorsquil rencontre un CONTROL -Z (octet 0x18) dans le fichier dentrée. Il sagit dun mode hérité des premiers jours DOS où le répertoire nenregistrait pas la longueur de le fichier et donc la longueur du fichier a été calculée en fonction du nombre de secteurs de 512 octets. Si vous pouvez dire à patch douvrir le fichier en mode binaire, il ne devrait pas ‘ avoir cette erreur.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *