Publicado por Chris Siebenmann , Administrador de Sistemas Unix en la Universidad de TorontoDe 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.