descubra quais descritores de arquivo compartilham a mesma "descrição de arquivo aberto"

17

Se eu fizer (em um shell tipo Bourne):

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

Os descritores de arquivo 3 e 4, desde que 4 foi dup()editado a partir de 3, compartilham a mesma descrição de arquivo aberto (mesmas propriedades, o mesmo deslocamento dentro do arquivo ...). Embora os descritores de arquivo 5 e 6 desse processo estejam em uma descrição diferente de arquivo aberto (por exemplo, cada um deles tem seu próprio ponteiro no arquivo).

Agora, na lsofsaída, tudo o que vemos é:

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

É um pouco melhor com 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

(aqui no Linux 3.16), pois vemos que o fd 6 tem sinalizadores diferentes; portanto, deve haver uma descrição de arquivo aberto diferente daquela do fd 3, 4 ou 5, mas disso não podemos dizer que o fd 5 está em um descrição diferente do arquivo aberto . Com -o, também pudemos ver o deslocamento, mas novamente o mesmo deslocamento não garante a mesma descrição de arquivo aberto .

Existe alguma não-intrusiva 1 maneira de descobrir isso? Externamente, ou para os próprios descritores de arquivo de um processo?


1 . Uma abordagem heurística poderia ser alterar os sinalizadores de um fd fcntl()e ver com que outros descritores de arquivos têm seus sinalizadores atualizados como resultado, mas isso obviamente não é o ideal nem a prova de idiotas

Stéphane Chazelas
fonte
Essa abordagem deve funcionar, em princípio, e não deve ser muito perturbadora na maioria dos cenários: primeiro bifurque uma criança (com ptrace se estiver fazendo isso de fora). Em seguida, na criança, faça algo com o descritor de arquivo que não afeta outros processos. No Linux, as concessões devem funcionar para isso.
Gilles 'SO- stop be evil'
@ Gilles, obrigado, mas essa é mais ou menos a abordagem que sugiro na pergunta. concessões (assumindo que você quer dizer o F_SETLEASE fcntl, obrigado por me informar sobre eles BTW) só funcionará para arquivos regulares que você possui e não se houver outra descrição de arquivo aberto "escrita" no mesmo arquivo (EBUSY), e não é exatamente não intrusivo.
Stéphane Chazelas 23/03
Você abandonou esta pergunta? Publiquei algumas informações sobre como o SystemTap poderia fazer o que você deseja, mas você não marcou nenhuma resposta como completa ...?
Azhrei 14/05

Respostas:

2

Para o Linux 3.5 e posteriores, isso pode ser realizado com o kcmp (3) :

KCMP_FILE

  • Verifique se um descritor de arquivo idx1 no processo pid1 se refere à mesma descrição de arquivo aberto (consulte open (2) ) que o descritor de arquivo idx2 no processo pid2 . A existência de dois descritores de arquivo que se referem à mesma descrição de arquivo aberto pode ocorrer como resultado da bifurcação dup (2) (e similar) (2) ou da passagem de descritores de arquivo por um soquete de domínio (consulte unix (7) ).

A página de manual fornece um exemplo específico para o OP de caso de uso solicitado. Note que este syscall requer que o kernel seja compilado com CONFIG_CHECKPOINT_RESTOREset.

mínimo máximo média
fonte
Obrigado. Exatamente o que eu estava procurando. Note-se que a menos que você está superusuário, tem que ser dois processos de seu (e não ser setuid / setgid ...) (compreensivelmente)
Stéphane Chazelas
@ StéphaneChazelas Exatamente. Se, por algum motivo, o suporte à CPIU não foi construído no seu kernel e você não deseja reconstruí-lo, suponho que você sempre possa escrever um módulo do kernel que exporte alguma interface do usuário que permita comparar struct file *ponteiros.
precisa saber é o seguinte
3

O que você deseja comparar são os struct fileindicadores apontados pelos descritores de arquivo. (Dentro do kernel há uma task_structestrutura de dados para cada thread. Ele contém um ponteiro para outra estrutura chamada the files_struct. E essa estrutura contém uma matriz de ponteiros, cada um para A. struct fileÉ o struct fileque retém o deslocamento de busca, os sinalizadores abertos e um alguns outros campos.)

Não conheço nenhuma maneira visível do usuário para ver os ponteiros files_structalém do uso de algumas ferramentas intrusivas. Por exemplo, o SystemTap pode receber um PID e encontrar o correspondente task_structe seguir os ponteiros. Se você está procurando passivo, acho que é isso. A Dell lançou uma ferramenta há muito tempo chamada KME (Kernel Memory Editor), que fornecia uma interface semelhante a uma planilha para armazenar a memória do kernel e poderia fazer o que você deseja, mas nunca foi portada para 64 bits. (Tentei e nunca o fiz funcionar completamente e não sabia ao certo por quê.)

Uma razão pela qual você não está achando lsofútil é que ele também não vê esses ponteiros (mas veja a +fopção para sistemas não Linux). Teoricamente, você pode comparar todos os campos struct filee pensar que as duas estruturas são iguais, mas ainda assim podem ser de open(2)chamadas separadas .

Dê uma olhada no script SystemTap pfiles para obter idéias. Se você o modificou para imprimir o endereço do struct file, teria sua solução. Você também pode verificar o arquivo open_file_by_pid.stp, já que há uma função nele que percorre o files_struct, ie. a tabela do descritor de arquivo, olhando para os struct fileobjetos ...

Posso perguntar o que você está tentando realizar?

Azhrei
fonte
Tenho que admitir que não me lembro exatamente do caso em que eu precisava disso. Alguma tarefa de depuração ou forense, sem dúvida.
Stéphane Chazelas
Estou ansioso para o código systemtap PoC :-)
Stéphane Chazelas
Antes de postar a pergunta, dei uma olhada nas abordagens systemtap ou / proc / kcore. A parte difícil era obter as informações de todos os detalhes de cada tarefa . A abordagem mais promissora que encontrei foi conectar-se às funções que geram o conteúdo do diretório / proc / * / task / fd, mas as únicas coisas viáveis ​​que pude criar envolvem a conexão com números de linhas específicos no arquivo de origem, para que não portátil de uma versão do kernel para a próxima. Você não pode realmente percorrer a lista de tarefas na systemtap. Talvez seja possível via / proc / kcore, mas muito esforço e provavelmente não confiável.
Stéphane Chazelas
Obrigado pela melhor resposta até agora. Vou dar uma olhada nos seus ponteiros.
Stéphane Chazelas
Certamente você pode! Configure um probe beginbloco e faça com que ele use a for_each_processmacro em um bloco de código C incorporado no script (você precisará usar o SystemTap no modo "guru" para incorporar o código C). De fato, para tornar isso interessante (!), Você pode usar uma das matrizes associativas do SystemTap; use o files_structendereço como chave e uma lista de PIDs / TIDs como valores. Agora você tem uma lista de todos os arquivos abertos e quais tarefas estão sendo compartilhadas (elas podem ser compartilhadas entre pai / filho). Responda novamente se quiser discutir o SystemTap.
Azhrei
0

Aqui está uma solução específica para Linux: / proc / self / fd é um diretório de links simbólicos para identificadores de arquivos abertos no processo atual. Você pode apenas comparar os valores do link. Fica mais complicado ao usar um processo filho, porque a criança terá um / proc / self diferente porque é um link simbólico dependente de pid. Você pode solucionar esse problema usando / proc / $$ / fd em que $$ é o pid desejado.

hildred
fonte
Obrigado. Mas não é isso que estou perguntando. No Linux, o lsof realmente usa / proc / pid / fd para recuperar caminhos para cada descritor de arquivo e / proc / pid / fdinfo para os sinalizadores. Mas o que eu quero é que, para dois fds no mesmo arquivo, aponte para a mesma descrição de arquivo aberto ou se os dois descritores de arquivo tenham sido abertos independentemente.
Stéphane Chazelas
ok, depois de encontrar pares de descritores de arquivos abertos para o mesmo nome de arquivo, informe-os e compare os resultados, se eles diferirem, eles são separados. Se eles são os mesmos, procure o descritor de um arquivo e repita: Se eles ainda corresponderem, são os mesmos.
Hildred 25/04/2015
Bem, essa é uma variante mais intrusiva da abordagem heurística a que me refiro na pergunta e que funciona apenas para arquivos regulares (não soquetes, dispositivos (como terminais), tubos ...).
Stéphane Chazelas