Recuperar o nome do arquivo do descritor de arquivo em C

105

É possível obter o nome do arquivo de um descritor de arquivo (Linux) em C?

adk
fonte
Acho que a resposta escolhida deve ser dada a zneak, pois sua solução tem melhor portabilidade e não apresenta problemas de acesso.
Sergei
Não é compatível com Ubuntu 14.04 (kernel 3.16.0-76-genérico). Eu estou supondo que não há suporte algum no Linux.
felipou,
Para macOS, veja esta resposta a outra pergunta de D.Nathanael .
Jonathan Leffler

Respostas:

120

Você pode usar readlinkem /proc/self/fd/NNNonde NNN é o descritor de arquivo. Isso fornecerá o nome do arquivo como estava quando foi aberto - entretanto, se o arquivo foi movido ou excluído desde então, ele pode não ser mais preciso (embora o Linux possa rastrear renomeações em alguns casos). Para verificar, stato nome do arquivo dado e fstato fd que você tem, e certifique-se st_deve st_inosão os mesmos.

Obviamente, nem todos os descritores de arquivo se referem a arquivos e, para eles, você verá algumas strings de texto estranhas, como pipe:[1538488]. Como todos os nomes de arquivos reais serão caminhos absolutos, você pode determinar quais são facilmente. Além disso, como outros notaram, os arquivos podem ter vários links físicos apontando para eles - isso só informará aquele com o qual foi aberto. Se você quiser encontrar todos os nomes de um determinado arquivo, terá apenas que percorrer todo o sistema de arquivos.

bdonlan
fonte
9
Contanto que o arquivo original ainda tenha referências a ele (uma abertura fdseria essa referência), o número do inode não pode ser reutilizado. Qualquer software que faça uso de um número de inode depois de fechar o arquivo ou antes de abri-lo está inerentemente sujeito a condições de corrida.
R .. GitHub PARAR DE AJUDAR O ICE
3
Perigo, Will Robinson! Isso nem sempre funciona --- se você fizer setuid()truques, é possível /proc/self/fdque seu processo não fique acessível. Veja: permalink.gmane.org/gmane.linux.kernel/1302546
David dado
2
@bdonlan: e no caso de / proc não estar montado?
user2284570
1
@ user2284570, esta resposta é específica do Linux. Não sei se o NetBSD oferece suporte a procfs - se o seu host compartilhado não oferece, provavelmente é porque o NetBSD não oferece suporte a ele e usa outro mecanismo. Você pode querer postar outra pergunta com foco no NetBSD para ver se alguém sabe como o NetBSD expõe essas informações (você também pode tentar a resposta do zneak abaixo, o OS X é mais semelhante ao BSD do que ao Linux)
bdonlan
1
@bdonlan: NetBSD support / proc mas não é obrigatório montá-lo. Cada vez que mencionei, a resposta se tornou "mude para um provedor de custo mais alto e você obterá / proc". Portanto, estou procurando uma solução procless.
user2284570
90

Tive esse problema no Mac OS X. Não temos um /procsistema de arquivos virtual, então a solução aceita não funciona.

Em vez disso, temos um F_GETPATHcomando para fcntl:

 F_GETPATH          Get the path of the file descriptor Fildes.  The argu-
                    ment must be a buffer of size MAXPATHLEN or greater.

Portanto, para obter o arquivo associado a um descritor de arquivo, você pode usar este snippet:

#include <sys/syslimits.h>
#include <fcntl.h>

char filePath[PATH_MAX];
if (fcntl(fd, F_GETPATH, filePath) != -1)
{
    // do something with the file path
}

Como nunca me lembro onde MAXPATHLENestá definido, pensei que PATH_MAXsyslimits seria bom.

zneak
fonte
@uchuugaka, provavelmente não. Use getsockname.
zneak de
2
O que você espera? A menos que seja um soquete UNIX, ele não possui nenhum arquivo associado.
zneak de
2
@uchuugaka Sim, tudo é um arquivo, mas nem tudo é uma entrada de diretório com um nome e uma localização dentro da árvore do sistema de arquivos. Um arquivo é representado por um inode, ele pode existir sem nenhuma entrada de diretório fazendo referência a ele.
lgeorget
9
Em <sys / param.h>: #define MAXPATHLEN PATH_MAX
geowar
1
Acabei de testar isso e permanece correto se o arquivo for movido e você chamá-lo novamente (ou seja: você obtém o novo caminho do arquivo). No entanto, isso não é suportado no Linux (testado no Ubuntu 14.04 - F_GETPATH ​​não está definido).
felipou,
27

No Windows, com GetFileInformationByHandleEx , passando FileNameInfo , você pode recuperar o nome do arquivo.

Martin v. Löwis
fonte
17
por mais que eu odeie windows, o equivalente do windows é sempre bom ter
Matt Joiner
15

Como Tyler aponta, não há como fazer o que você precisa "direta e confiável", uma vez que um determinado FD pode corresponder a 0 nomes de arquivo (em vários casos) ou> 1 (vários "links físicos" é como a última situação é geralmente descrita ) Se você ainda precisa da funcionalidade com todas as limitações (na velocidade E na possibilidade de obter 0, 2, ... resultados em vez de 1), veja como você pode fazer isso: primeiro, fstat o FD - isso lhe diz , no resultado struct stat, em qual dispositivo o arquivo reside, quantos links físicos ele possui, se é um arquivo especial, etc. Isso já pode responder à sua pergunta - por exemplo, se 0 links físicos você SABER que de fato não há nenhum nome de arquivo correspondente no disco.

Se as estatísticas te dão esperança, então você tem que "andar na árvore" de diretórios no dispositivo relevante até encontrar todos os links físicos (ou apenas o primeiro, se você não precisar de mais de um e qualquer um servirá ) Para esse propósito, você usa readdir (e opendir & c, é claro) abrindo subdiretórios recursivamente até encontrar em um, struct direntportanto, recebeu o mesmo número de inode que você tinha no original struct stat(nesse momento, se você quiser todo o caminho, em vez de apenas o nome, você precisará percorrer a cadeia de diretórios para trás para reconstruí-la).

Se esta abordagem geral for aceitável, mas você precisar de um código C mais detalhado, informe-nos, não será difícil escrever (embora eu prefira não escrevê-lo se for inútil, ou seja, você não pode suportar o desempenho inevitavelmente lento ou o possibilidade de obter! = 1 resultado para os fins de sua aplicação ;-).

Alex Martelli
fonte
9

Antes de descartar isso como impossível, sugiro que você examine o código-fonte do comando lsof .

Pode haver restrições, mas lsof parece capaz de determinar o descritor e o nome do arquivo. Essas informações existem no sistema de arquivos / proc, portanto, deve ser possível obtê-las em seu programa.

Pato
fonte
6

Você pode usar fstat () para obter o inode do arquivo por struct stat. Então, usando readdir () você pode comparar o inode que você encontrou com aqueles que existem (struct dirent) em um diretório (assumindo que você conhece o diretório, caso contrário, você terá que pesquisar todo o sistema de arquivos) e encontrar o nome do arquivo correspondente. Desagradável?

PetrosB
fonte
2

Impossível. Um descritor de arquivo pode ter vários nomes no sistema de arquivos ou pode não ter nenhum nome.

Edit: Supondo que você esteja falando sobre um sistema POSIX simples e antigo, sem APIs específicas do sistema operacional, já que você não especificou um sistema operacional.

Tyler McHenry
fonte
4
então minha resposta se aplica. O Linux não tem recursos para fazer isso. Os descritores de arquivo do Linux (POSIX) não se referem necessariamente a arquivos e, mesmo que o façam, eles se referem a inodes, não a nomes de arquivos. Um descritor pode apontar para um arquivo excluído (que, portanto, não tem nome, esta é uma maneira comum de criar arquivos temporários) ou pode apontar para um inode com vários nomes (links físicos).
Tyler McHenry
3
Tente dar uma olhada no código-fonte lsof. :) Isso é o que eu fiz quando eu mesmo tive essa mesma pergunta um tempo atrás. Isof funciona com magia negra e cabras sacrificais - você não pode esperar duplicar seu comportamento. Para ser mais específico, lsof é fortemente acoplado ao kernel do Linux e não faz o que faz por meio de qualquer API que esteja disponível para o código do usuário.
Tyler McHenry
27
O Linux tem uma API proc não portátil para isso. De fato, existem limitações, mas dizer que é impossível é simplesmente falso.
bdonlan
1
@Tyler - lsof é executado no espaço do usuário. Portanto, há uma API disponível para o código do userland :)
bdonlan
1
@Duck, a portabilidade provavelmente é o motivo pelo qual a fonte de lsof tem tanta magia negra; cada variante do UNIX faz isso de maneira diferente. As interfaces proc do Linux não são tão ruins, na verdade, embora estejam escassamente documentadas.
bdonlan