averiguar qué descriptores de archivo comparten la misma "descripción de archivo abierto"

17

Si hago (en una cáscara de Bourne-like):

exec 3> file 4>&3 5> file 6>> file

Los descriptores de archivo 3 y 4, dado que 4 se dup()editó a partir de 3, comparten la misma descripción de archivo abierto (mismas propiedades, mismo desplazamiento dentro del archivo ...). Mientras que los descriptores de archivo 5 y 6 de ese proceso están en una descripción de archivo abierta diferente (por ejemplo, cada uno tiene su propio puntero en el archivo).

Ahora, en la lsofsalida, todo lo que vemos es:

zsh     21519 stephane    3w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    4w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    5w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    6w   REG  254,2        0 10505865 /home/stephane/file

Es un poco mejor con lsof +fg:

zsh     21519 stephane    3w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    4w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    5w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    6w   REG       W,AP,LG  254,2        0 10505865 /home/stephane/file

(aquí en Linux 3.16) en el que vemos que fd 6 tiene diferentes indicadores, por lo que debe ser una descripción de archivo abierto diferente de la de fd 3, 4 o 5, pero a partir de eso no podemos decir que fd 5 está en un Descripción de archivo abierto diferente . Con -o, también podríamos ver el desplazamiento, pero de nuevo el mismo desplazamiento no garantiza que sea la misma descripción de archivo abierto .

¿Hay alguna forma no intrusiva de descubrirlo? Externamente, o para un proceso de propios descriptores de archivos?


1 . Un enfoque heurístico podría ser cambiar las marcas de un fcntl()archivo con y ver qué otros descriptores de archivos tienen sus banderas actualizadas como resultado, pero eso obviamente no es ideal ni a prueba de tontos

Stéphane Chazelas
fuente
Este enfoque debería funcionar, en principio, y no ser demasiado perjudicial en la mayoría de escenarios: en primer tenedor un niño (con ptrace si lo hace desde el exterior). Luego, en el niño, haga algo con el descriptor de archivo que no afecte a otros procesos. En Linux, los arrendamientos deberían funcionar para eso.
Gilles 'SO- deja de ser malvado'
@Gilles, gracias, pero ese es más o menos el enfoque que sugiero en la pregunta. contratos de arrendamiento (suponiendo que se refiere al F_SETLEASE fcntl, gracias por informarme de ellos BTW) solo funcionará para los archivos normales que posee y no si hay otra descripción de archivo abierto "escrito" en el mismo archivo (EBUSY), y no es exactamente no -intruso.
Stéphane Chazelas
¿Ha abandonado esta pregunta? Publiqué información sobre cómo SystemTap podría hacer lo que desea, pero no ha marcado ninguna respuesta como completa ...
Azhrei

Respuestas:

2

Para Linux 3.5 y versiones posteriores, esto se puede lograr con kcmp (3) :

KCMP_FILE

  • Compruebe si un descriptor de archivo idx1 en el proceso pid1 se refiere a la misma descripción de archivo abierto (vea open (2) ) que el descriptor de archivo idx2 en el proceso pid2 . La existencia de dos descriptores de archivo que hacen referencia a la misma descripción de archivo abierto puede ocurrir como resultado de una bifurcación dup (2) (y similar) (2) , o al pasar descriptores de archivo a través de un socket de dominio (ver unix (7) ).

La página del manual proporciona un ejemplo específico para el caso de uso que solicitó OP. Tenga en cuenta que esta llamada al sistema requiere que el núcleo se compile con CONFIG_CHECKPOINT_RESTOREset.

mínimo máximo promedio
fuente
Gracias. Exactamente lo que estaba buscando. Tenga en cuenta que a menos que sea un superusuario, tiene que ser dos procesos suyos (y no ser setuid / setgid ...) (comprensiblemente)
Stéphane Chazelas
@ StéphaneChazelas Exactamente. Si por alguna razón el soporte de CPIU no se creó en su kernel y no desea reconstruirlo, entonces supongo que siempre puede escribir un módulo de kernel que exporte alguna interfaz de usuario que le permita comparar struct file *punteros.
minmaxavg
3

Lo que está buscando para comparar son los struct filepunteros a los que apuntan los descriptores de archivo. (Dentro del núcleo hay una task_structestructura de datos para cada subproceso. Contiene un puntero a otra estructura llamada files_struct. Y esa estructura contiene una matriz de punteros, cada uno a struct file. Es el struct fileque contiene el desplazamiento de búsqueda, las banderas abiertas y un algunos otros campos)

No conozco ninguna forma visible para el usuario de ver los punteros files_structque no sean el uso de algunas herramientas intrusivas. Por ejemplo, SystemTap podría recibir un PID y podría encontrar el correspondiente task_structy seguir los punteros. Sin embargo, si buscas pasivo, creo que eso es todo. Dell lanzó una herramienta hace mucho tiempo llamada KME (Kernel Memory Editor) que brindaba una interfaz similar a una hoja de cálculo para vivir la memoria del kernel y podía hacer lo que deseaba, pero nunca se transfirió a 64 bits. (Lo intenté y nunca lo hice funcionar por completo, y no estaba seguro de por qué).

Una razón usted no está encontrando lsofpara ser útil es que no ve los punteros o bien (pero mira la +fopción para los sistemas no Linux). Teóricamente, podría comparar todos los campos en el struct filey pensar que las dos estructuras son iguales, pero aún podrían ser de open(2)llamadas separadas .

Eche un vistazo al script SystemTap de pfiles para obtener ideas. Si lo modificó para imprimir la dirección del struct file, tendría su solución. También puede verificar abrir_archivo_por_pid.stp ya que hay una función que recorre el files_struct, es decir. la tabla de descriptores de archivos, mirando los struct fileobjetos ...

¿Puedo preguntarte qué estás tratando de lograr?

Azhrei
fuente
Tengo que admitir que no recuerdo el caso en el que lo necesitaba. Algunas tareas de depuración o forenses, sin duda.
Stéphane Chazelas
Estoy deseando que llegue el código PoC systemtap :-)
Stéphane Chazelas
Antes de publicar la pregunta, eché un vistazo a los enfoques systemtap o / proc / kcore. La parte difícil fue obtener la información para cada fd de cada tarea . El enfoque más prometedor que se encontró fue enganchar en las funciones que generan el contenido del directorio / proc / * / tarea / fd, pero las cosas solamente factibles que podría llegar a enganchar involucrados en línea números específicos en el archivo de origen por lo que no portátil de una versión de kernel a la siguiente. Realmente no puede recorrer la lista de tareas en systemtap. Tal vez sea posible a través de / proc / kcore, pero demasiado esfuerzo y probablemente no confiable.
Stéphane Chazelas
Gracias por la mejor respuesta hasta ahora. Echaré un vistazo a tus consejos.
Stéphane Chazelas
¡Seguro que puede! Configure un probe beginbloque y haga que use la for_each_processmacro en un bloque de código C incrustado en el script (necesitará usar SystemTap en modo "guru" para incrustar código C). De hecho, para hacer esto interesante (!), Podría usar una de las matrices asociativas de SystemTap; use la files_structdirección como clave y una lista de PID / TID como valores. Ahora tiene una lista de todos los archivos abiertos y qué tareas los comparten (se pueden compartir entre padres / hijos). Responda nuevamente si desea hablar sobre SystemTap.
Azhrei
0

Aquí es una solución específica Linux: / proc / self / fd es un directorio de enlaces simbólicos para los manejadores de archivos abiertos en el proceso actual. Simplemente puede comparar los valores del enlace. Se vuelve más complicado cuando se utiliza un proceso hijo, porque el niño tendrá un / proc / self diferente porque es un enlace simbólico depende pid. Puede solucionar este problema utilizando / proc / $$ / fd donde $$ es el pid deseado.

Hildred
fuente
Gracias. Pero eso no es lo que estoy preguntando. En Linux, lsof sí usa / proc / pid / fd para recuperar rutas para cada descriptor de archivo y / proc / pid / fdinfo para las banderas. Pero lo que quiero es que para dos fds en el mismo archivo si apuntan a la misma descripción de archivo abierto o si los dos descriptores de archivo se han abierto de forma independiente.
Stéphane Chazelas
ok, después de haber encontrado pares de descriptores de archivo que están abiertos para el mismo nombre de archivo, haga un informe sobre ambos y compare los resultados, si difieren, están separados. Si son iguales, busque en un descriptor de archivo y repita, si aún coinciden, son lo mismo.
hildred
Bueno, esa es una variante más intrusiva del enfoque heurístico al que me refiero en la pregunta y que solo funciona para archivos normales (no sockets, dispositivos (como terminales), tuberías ...).
Stéphane Chazelas