Nuevamente sobre las abstracciones holey (o sobre un entorno impredecible)

Entonces, una parte bastante simple del programa para Windows. Hay un archivo que contiene varias entradas. Y necesitan ser filtrados de cierta manera.

La solución es bastante simple: abra el archivo, lea los registros uno por uno, escribimos los necesarios en un archivo temporal. Cierra el archivo. Lo eliminamos Cambie el nombre del temporal al original. Todo es tan simple que ni siquiera daré el código. ¿Es realmente esta razón suficiente para el artículo?

Si bien todo funciona, realmente no hay ninguna razón para escribir al respecto. Pero de repente un día "todo cae", porque el cambio de nombre no se produce debido a un error de acceso denegado. Esto ocurre muy raramente, pero aún más a menudo para sospechar rayos cósmicos.

Comenzamos la excavación. La primera pista encontrada: en el proceso de excavación, esta cita de la documentación de Microsoft alertó:
La función DeleteFile marca un archivo para su eliminación al cerrar. Por lo tanto, la eliminación del archivo no se produce hasta que se cierra el último identificador del archivo. Las llamadas posteriores a CreateFile para abrir el archivo fallan con ERROR_ACCESS_DENIED.
En términos de síntomas, es muy similar, pero solo ¿de dónde provienen estos "otros controladores" si, aparte de nosotros, nadie hace y no debería hacer nada con este archivo? ¿Y no tenemos otros hilos con hilos que hagan algo con este archivo?

El culpable fue encontrado gracias a SysInternals y su ProcessMonitor. Lo lanzamos, instalamos el filtro en nuestro archivo sufrido y tratamos de reproducirlo durante un tiempo prolongado y persistente. Nosotros reproducimos Nosotros miramos. ¿Y qué vemos allí?
01.2: 30: 28.3162097 PM our_prog.exe 1288 CreateFile our.file ÉXITO Acceso deseado: lectura / escritura genérica
02.2: 25: 28.3164513 PM our_prog.exe 1288 WriteFile our.file ÉXITO Offset: 0, Longitud: 898, Prioridad: Normal
...
34.2: 25: 28.3173405 PM our_prog.exe 1288 WriteFile our.file ÉXITO Offset: 35,290, Longitud: 1,113
35.2: 25: 28.3173493 PM our_prog.exe 1288 WriteFile our.file ÉXITO Offset: 36,403, Longitud: 1,128
36.2: 25: 28.3173736 PM our_prog.exe 1288 FlushBuffersFile our.file ÉXITO
37.2: 25: 28.3174212 PM our_prog.exe 1288 WriteFile our.file ÉXITO Offset: 0, Longitud: 40,960,
38.2: 25: 28.3175927 PM Explorer.EXE 1884 QueryBasicInformationFile our.file SUCCESS
39.2: 25: 28.3176144 PM Explorer.EXE 1884 CerrarArchivo our.file ÉXITO
40.2: 25: 28.3263642 PM Explorer.EXE 1884 CreateFile our.file ÉXITO Acceso deseado: Leer atributos,
41.2: 25: 28.3294990 PM our_prog.exe 1288 Cerrar Archivo our.file ÉXITO
42.2: 25: 28.3351356 PM our_prog.exe 1288 CreateFile our.file ÉXITO Acceso deseado: Leer atributos, Eliminar,
43.2: 25: 28.3351856 PM our_prog.exe 1288 QueryAttributeTagFile our.file ÉXITO Atributos: A, ReparseTag: 0x0
44.2: 25: 28.3352020 PM our_prog.exe 1288 SetDispositionInformationFile our.file ÉXITO Eliminar: True
45.2: 25: 28.3352218 PM our_prog.exe 1288 Cerrar Archivo our.file ÉXITO
46.2: 25: 28.3358275 PM our_prog.exe 1288 CreateFile our.file ELIMINAR PENDIENTE Acceso deseado: lectura / escritura genérica,
47.2: 25: 28.3362207 PM our_prog.exe 1288 CreateFile our.file ELIMINAR PENDIENTE Acceso deseado: lectura / escritura genérica,
48.2: 25: 28.3367696 PM Explorer.EXE 1884 QueryBasicInformationFile our.file SUCCESS
49.2: 25: 28.4279152 PM Explorer.EXE 1884 CerrarArchivo our.file ÉXITO
50.2: 25: 28.4282859 PM Explorer.EXE 1884 CreateFile our.file NOMBRE NO ENCONTRADO Acceso deseado: Leer atributos,
...
83.2: 25: 29.3497760 PM our_prog.exe 1288 CreateFile our.file ÉXITO Acceso deseado: lectura / escritura genérica,
Y vemos lo siguiente allí (se eliminaron datos adicionales para no saturar). Líneas 1 a 36: creamos un archivo, le escribimos, hacemos vaciado. Lo más interesante comienza en las líneas 38-40. Explorer.exe aparece de la nada y comienza a leer nuestro archivo.

En la línea 41 cerramos nuestro archivo. En la línea 42: eliminar. Y dado que explorer.exe todavía lo está leyendo, el archivo no se elimina. Lo que podemos ver en las líneas 46 y 47 cuando intentamos cambiar el nombre de nuestro archivo temporal al principal (BORRAR estado PENDIENTE en lugar de ÉXITO).

Explorer.exe termina de leer solo en la línea 49. Solo en ese momento el archivo se elimina físicamente (como nos dice indirectamente la línea 50, donde nuestro persistente explorer.exe nuevamente intenta abrir el archivo para leerlo, pero falla porque el archivo ya no)

¿Qué se sigue de esto? Gracias a Microsoft Windows, incluso una simple operación de eliminación de archivos ahora debe realizarse al estilo de la programación paranoica. Llamaron a la función de eliminación, se aseguraron de que devolviera OK e ingresaron al ciclo de espera "hasta que el archivo se haya eliminado físicamente". Bueno y sí, sin saber que "ella tiene un neón adentro", casi nada se puede hacer ...

Pero ahora la duda me está corroyendo que ese enfoque es una práctica generalmente aceptada. Para tener en cuenta durante cualquier trabajo con archivos en Windows que el sistema operativo en cualquier momento decide hacer algo con los archivos sin su conocimiento, y escribir código que sea resistente a esto. En este sentido, la encuesta es más baja.

Source: https://habr.com/ru/post/450700/


All Articles