Cómo encontré un error en GNU Tar

Publicado por Chris Siebenmann , Administrador de Sistemas Unix en la Universidad de Toronto

De vez en cuando, sucede algo extraño en mi trabajo que me hace pensar. Incluso si no está claro de inmediato qué conclusiones siguen. Recientemente mencioné que encontramos un error en GNU Tar, y la historia de cómo sucedió esto es uno de esos casos.

Para los servidores de archivos de respaldo, utilizamos Amanda y GNU Tar. Con el tiempo, ocasionalmente tuvimos un problema bastante raro en el que tar se volvió loco al hacer una copia de seguridad del sistema de archivos con el directorio /var/mail , produciendo una gran cantidad de resultados. Usualmente este proceso fue al infinito y tuvo que matar el basurero; en otros casos, todavía terminó emitiendo terabytes de datos que parecían estar perfectamente comprimidos. Cuando una vez más me encontré con un archivo tar tan gigante, lo revisé y descubrí que en parte consta de cero bytes, que al equipo de prueba de tar -t realmente no le gusta, después de lo cual todo vuelve a la normalidad.

(Debido a esto, me preguntaba si los bytes nulos aparecen naturalmente en las personas en los buzones. Resultó que encontrar bytes nulos en los archivos de texto no es tan simple y sí, están allí).

Recientemente cambiamos el sistema de archivos de /var/mail a los nuevos servidores de archivos de Linux en Ubuntu 18.04 y, por lo tanto, cambiamos a una versión posterior y más estándar de GNU Tar que en las máquinas OmniOS. Esperábamos que esto resolviera nuestros problemas, pero el mismo incidente ocurrió casi de inmediato. Esta vez, GNU Tar trabajó en una máquina Ubuntu, donde conozco bien todas las herramientas de depuración disponibles, así que verifiqué el proceso de ejecución de tar . La prueba mostró que tar produce una secuencia interminable de read() devuelve 0 bytes:

 read(6, "", 512) = 0 read(6, "", 512) = 0 [...] read(6, "", 512) = 0 write(1, "\0\0\0\0\0"..., 10240) = 10240 read(6, "", 512) = 0 [...] 

lsof dicho descriptor de archivo 6 es el buzón de otra persona.

Usando apt-get source tar descargué el código fuente y comencé a buscar llamadas al sistema read() que no verificaran la finalización del archivo. Después de examinar varios niveles de direccionamiento indirecto, encontré un lugar obvio en el que parece que se omitió tal verificación, es decir, en la función sparse_dump_region del archivo sparse.cs . Y luego recordé algo.

Hace unos meses, encontramos un problema de NFS en Alpine . Mientras trabajaba en este error, rastreé el proceso Alpine y noté, entre otras cosas, que usa ftruncate() para cambiar el tamaño de los buzones; a veces los expande, creando temporalmente una sección dispersa del archivo hasta que lo llena, y tal vez a veces lo comprime. Esto parecía coincidir con la situación actual: las áreas dispersas están conectadas y la reducción del tamaño del archivo usando ftruncate() crea una situación en la que tar encuentra inesperadamente una finalización del archivo.

(Esto incluso explica por qué a veces se restaura el alquitrán; si el correo nuevo llega repentinamente a la casilla más tarde, vuelve al tamaño esperado y el alquitrán ya no encuentra una finalización inesperada del archivo).

Me equivoqué un poco en GDB con los símbolos de depuración de Ubuntu y el código fuente del paquete tar que recibí, y pude reproducir el error, aunque era algo diferente de mi teoría original. Resultó que sparse_dump_region no restablece las áreas dispersas del archivo, sino que restablece las áreas no dispersas (por supuesto), y se usa para todos los archivos (dispersos o no) si ejecuta tar con el argumento --sparse . Por lo tanto, el error real es que si ejecuta GNU Tar con el argumento --sparse y el archivo se comprime a medida que se lee, tar no puede manejar correctamente el final del archivo recibido antes de lo esperado . Si el archivo vuelve a crecer, el tar se restaura.

(Excepto cuando el archivo es escaso solo al final y comprimido solo en este lugar. En este caso, todo está en orden).

Pensé que, de todos modos, podía comprobar hace muchos años en nuestros servidores de archivos OmniOS. Hay maneras de rastrear las llamadas al sistema del programa y lsof análogos, y pude encontrar y mirar el código fuente de mi versión de GNU Tar y ejecutarlo con el depurador OmniOS (aunque no parece que tengamos GDB instalado allí), y así sucesivamente. Pero no lo hice. En cambio, nos encogimos de hombros y seguimos adelante. Me llevó mover el sistema de archivos en Ubuntu para poder mover el dedo y resolver el problema.

(No se trata solo de las herramientas y el entorno; también asumimos automáticamente que OmniOS tenía algún tipo de versión antigua no admitida de GNU Tar, que no tiene sentido investigar, porque, por supuesto, el problema se resolvió en la versión más reciente).

PD: Probablemente, como una solución rápida, simplemente prohibimos que Amanda use la --sparse tar --sparse cuando realiza una copia de seguridad. Los buzones no deben ser escasos, y si esto sucede, aún comprimimos las copias de seguridad del sistema de archivos , de modo que todos estos bytes cero estén bien comprimidos.

PPS: no intenté informar el error a los desarrolladores de GNU Tar, porque lo descubrí solo el viernes y la universidad está de vacaciones de invierno ahora. Siéntete libre de hacerlo antes que yo.

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


All Articles