Cause du contenu du fichier corrompu

J’ai un problème récurrent avec une application dans la nature.

Il contient un fichier XML assez simple qu’il vide de temps en temps, quelque chose comme toutes les 30 minutes.

Les fichiers de données sont souvent assez petits, par exemple <5 Ko.

Il ne maintient pas un verrou sur le fichier – il ne fait que le recréer à partir de zéro à chaque fois.

J’ai eu la chance de voir le problème se produire sur une machine de test et ce que j’ai observé, c’est que le fichier était corrompu et mis à “nulls” (c’est-à-dire 00 dans Hex). Ce qui est vraiment étrange, c’est que c’est exactement la bonne longueur par rapport à ce qu’elle aurait dû être.

J’ai essayé d’être très prudent dans mon processus de sauvegarde:

  1. J’écris le fichier XML dans un fichier temporaire dans le même répertoire que je vais vraiment le sauvegarder
  2. J’effectue un Win32 MoveFile () avec l’ensemble MOVEFILE_WRITE_THROUGH (il doit donc restr bloqué jusqu’à ce que le déplacement soit vraiment complet), pour déplacer le fichier afin de remplacer le fichier de données existant

Je verrouille même un Mutex pour m’assurer qu’il ne s’agit pas d’un problème de threading.

Cela n’arrive pas souvent, comme peut-être 1 utilisateur sur 1000.

Maintenant, dans le passé, j’ai observé des fichiers de données corrompus par une panne de courant ou un BSOD lors de l’écriture, et j’ai vu des choses comme le 32kb d’un fichier étant tout NULL.

Mais il semble juste que cela se produise plus que ce à quoi je m’attendais, étant donné les risques de coupure de courant pendant l’écriture, et en particulier depuis que j’utilise MOVEFILE_WRITE_THROUGH.

Des idées?

John


Réponses à quelques questions:

  • Q: Pourquoi ne pas écrire directement dans le fichier? A: Je l’ai évité pour rendre le logiciel moins vulnérable aux problèmes de panne de courant. Par exemple, vous êtes à mi-chemin dans l’écriture du fichier et de crash / powerfail / BSOD, alors vous avez certainement un fichier corrompu. Faire un fichier temporaire en écriture puis en déplacement est un moyen simple et couramment utilisé pour vous assurer que vous effectuez une opération de fichier atomique le plus possible (enfin, aussi proche que possible sans utiliser des API spécifiques à NTFS). Je devrais dire que le logiciel est un système d’archivage / sauvegarde, alors je dois faire plus attention à la cohérence des données que d’autres applications.

  • Q: Cela se produit-il pendant le fonctionnement normal?

  • R: Comme ce problème se pose à l’état sauvage, je ne travaille qu’avec quelques indices, alors je ne sais pas avec certitude. Je peux dire que le logiciel fonctionne à 99,9% du temps. J’imagine que c’est le nœud de ma question: est-ce juste un manque de chance aléatoire causé par une panne de BSOD ou une panne de courant?

  • Q: Quel environnement / OS:

  • A: XP, Vista, 7, Server 200X. Très probablement NTFS, mais pourrait être FAT32

  • Q: Est-ce que je ferme le fichier avant de déplacer

  • Un: oui. J’utilise les stream C ++ et appelle close () avant de faire le MoveFile

  • Q: Quels autres processus accèdent au fichier?

  • A: Aucun géré par moi. De toute évidence, je ne contrôle pas Virus Checker, Folder Syncers, etc. Le fichier se trouve dans le dossier AppData \ Local de la machine de l’utilisateur.

Comme mon expérience, il est possible que ce soit par cache de fichiers dans Windows. Vous devriez essayer de sauvegarder le fichier en utilisant CreateFile() avec FILE_FLAG_WRITE_THROUGH pass in. Enregistrer le fichier de cette façon peut vous assurer que le fichier va atterrir sur le disque dur.

J’avais écrit un petit programme pour le tester. Si le programme crée un fichier avec std::ofstream et utilise MoveFileEx() avec MOVEFILE_WRITE_THROUGH pour déplacer ce fichier, le fichier est corrompu presque chaque fois s’il est hors tension (et non pas arrêté) immédiatement après la fin du fichier; Sinon, si le programme utilise CreateFile() avec FILE_FLAG_WRITE_THROUGH pour créer un fichier et refait la même chose, le fichier n’a pas été corrompu (j’ai testé environ 10 fois, mais cela ne s’est pas produit).

Après ces tests simples, je pense que vous devriez essayer d’utiliser CreateFile() avec FILE_FLAG_WRITE_THROUGH pour résoudre votre problème.

Plus d’information:
Mise en cache de fichiers (Windows)
Windows Internals, 6ème édition, Chapitre 11 Cache Manager

Voici quelques idées:

  • Vider le stream après des informations critiques ou avant de longues périodes sans écriture.
  • Vérifiez qu’aucune autre entité n’écrit dans le fichier.
  • Vérifiez que les données mises en mémoire tampon ne sont pas écrasées par un autre code.
  • Fermez le fichier entre de longues durées sans écriture.