Donc, une partie assez simple du programme pour Windows. Il existe un fichier contenant plusieurs entrées. Et ils doivent être filtrés d'une certaine manière.
La solution est assez simple - ouvrez le fichier, lisez les enregistrements un par un, nous écrivons les fichiers nécessaires dans un fichier temporaire. Fermez le fichier. Nous le supprimons. Renommez le temporaire en original. Tout est si simple que je ne donnerai même pas le code. Est-ce vraiment une raison suffisante pour l'article?
Bien que tout fonctionne, il n'y a vraiment aucune raison d'écrire à ce sujet. Mais soudain, un jour «tout tombe», car le changement de nom ne se produit pas en raison d'une erreur d'accès refusé. Cela arrive très rarement, mais encore beaucoup plus souvent pour suspecter des rayons cosmiques.
Nous commençons l'excavation. Le premier indice a trouvé: en train de creuser, cette citation de la documentation Microsoft alertait:
La fonction DeleteFile marque un fichier à supprimer à la fermeture. Par conséquent, la suppression du fichier ne se produit que lorsque la dernière poignée du fichier est fermée. Les appels suivants à CreateFile pour ouvrir le fichier échouent avec ERROR_ACCESS_DENIED.
En termes de symptômes, c'est très semblable, mais d'où viennent ces "autres gestionnaires", si, à part nous, personne ne fait et ne doit rien faire avec ce fichier? Et nous n'avons pas d'autres threads avec des threads qui feraient quelque chose avec ce fichier?
Le coupable a été trouvé grâce à SysInternals et à leur ProcessMonitor. Nous le lançons, installons le filtre sur notre fichier qui souffre depuis longtemps et essayons de le reproduire pour une longue et persistante. Nous reproduisons. Nous regardons. Et que voyons-nous là-bas?
01.2: 30: 28.3162097 PM our_prog.exe 1288 CreateFile our.file SUCCÈS Accès souhaité: lecture / écriture générique
02.2: 25: 28.3164513 PM our_prog.exe 1288 WriteFile our.file SUCCÈS Offset: 0, Longueur: 898, Priorité: Normal
...
34.2: 25: 28.3173405 PM our_prog.exe 1288 WriteFile our.file SUCCESS Offset: 35,290, Longueur: 1,113
35.2: 25: 28.3173493 PM our_prog.exe 1288 WriteFile our.file SUCCÈS Offset: 36.403, Longueur: 1.128
36.2: 25: 28.3173736 PM our_prog.exe 1288 FlushBuffersFile our.file SUCCÈS
37.2: 25: 28.3174212 PM our_prog.exe 1288 WriteFile our.file SUCCESS Offset: 0, Longueur: 40,960,
38.2: 25: 28.3175927 PM Explorer.EXE 1884 QueryBasicInformationFile our.file SUCCESS
39.2: 25: 28.3176144 PM Explorer.EXE 1884 CloseFile our.file SUCCÈS
40.2: 25: 28.3263642 PM Explorer.EXE 1884 CreateFile our.file SUCCÈS Accès souhaité: Lire les attributs,
41.2: 25: 28.3294990 PM our_prog.exe 1288 CloseFile our.file SUCCÈS
42.2: 25: 28.3351356 PM our_prog.exe 1288 CreateFile our.file SUCCÈS Accès souhaité: Lire les attributs, Supprimer,
43.2: 25: 28.3351856 PM our_prog.exe 1288 QueryAttributeTagFile our.file SUCCESS Attributs: A, ReparseTag: 0x0
44.2: 25: 28.3352020 PM our_prog.exe 1288 SetDispositionInformationFile our.file SUCCESS Delete: True
45.2: 25: 28.3352218 PM our_prog.exe 1288 CloseFile our.file SUCCÈS
46.2: 25: 28.3358275 PM our_prog.exe 1288 CreateFile our.file SUPPRIMER EN ATTENTE Accès souhaité: lecture / écriture générique,
47.2: 25: 28.3362207 PM our_prog.exe 1288 CreateFile our.file SUPPRIMER EN ATTENTE Accès souhaité: lecture / écriture générique,
48.2: 25: 28.3367696 PM Explorer.EXE 1884 QueryBasicInformationFile our.file SUCCESS
49.2: 25: 28.4279152 PM Explorer.EXE 1884 CloseFile our.file SUCCÈS
50.2: 25: 28.4282859 PM Explorer.EXE 1884 CreateFile our.file NOM NOT FOUND Accès souhaité: Lire les attributs,
...
83.2: 25: 29.3497760 PM our_prog.exe 1288 CreateFile our.file SUCCÈS Accès souhaité: lecture / écriture générique,
Et nous voyons ce qui suit (les données supplémentaires ont été supprimées afin de ne pas encombrer). Lignes 1 à 36 - on crée un fichier, on y écrit, on fait flush. Le plus intéressant commence aux lignes 38-40. Explorer.exe apparaît de nulle part et commence à lire notre fichier.
À la ligne 41, nous fermons notre dossier. À la ligne 42 - supprimer. Et comme explorer.exe le lit toujours, le fichier n'est pas supprimé. Ce que nous pouvons voir aux lignes 46 et 47 lorsque nous essayons de renommer notre fichier temporaire en fichier principal (état DELETE PENDING au lieu de SUCCESS).
Explorer.exe termine la lecture uniquement à la ligne 49. Ce n'est qu'à ce moment que le fichier est physiquement supprimé (comme nous l'indique indirectement la ligne 50, où notre explorer.exe persistant tente à nouveau d'ouvrir le fichier en lecture, mais il échoue car le fichier plus).
Qu'est-ce qui en découle? Grâce à Microsoft Windows, même une simple opération de suppression de fichier doit maintenant être effectuée dans le style de la programmation paranoïaque. Ils ont appelé la fonction de suppression, ont vérifié qu'elle revenait bien et sont entrés dans le cycle d'attente «jusqu'à ce que le fichier ait été physiquement supprimé». Eh bien et oui, sans savoir "qu'elle a un néon à l'intérieur", presque rien ne peut être fait ...
Mais maintenant, le doute me ronge qu'une telle approche est une pratique généralement acceptée. Afin de garder à l'esprit lors de tout travail avec des fichiers dans Windows que le système d'exploitation décide à tout moment de faire quelque chose avec les fichiers à votre insu et d'écrire du code qui y résiste. À cet égard, l'enquête est plus faible.