Pobierz nazwę pliku z deskryptora pliku w C

105

Czy można uzyskać nazwę pliku deskryptora pliku (Linux) w C?

adk
źródło
Wydaje mi się, że wybrana odpowiedź powinna zostać udzielona zneakowi, ponieważ jego rozwiązanie jest bardziej przenośne i nie ma zauważalnych problemów z dostępem.
Siergiej
Nie jest obsługiwany w systemie Ubuntu 14.04 (jądro 3.16.0-76-generic). Domyślam się, że w ogóle nie jest obsługiwany w systemie Linux.
felipou
W przypadku systemu macOS zapoznaj się z tą odpowiedzią na inne pytanie zadane przez D.Nathanael .
Jonathan Leffler

Odpowiedzi:

120

Można używać readlinkna /proc/self/fd/NNNgdzie NNN jest deskryptor pliku. W ten sposób otrzymasz nazwę pliku taką, jaka była w momencie otwarcia - jednak jeśli plik został przeniesiony lub usunięty od tego czasu, może już nie być dokładny (chociaż w niektórych przypadkach Linux może śledzić zmiany nazw). Aby zweryfikować, statnazwa pliku podana i fstatFD masz, i upewnić się, st_devi st_inosą takie same.

Oczywiście nie wszystkie deskryptory plików odnoszą się do plików, a dla nich zobaczysz dziwne ciągi tekstowe, takie jak pipe:[1538488]. Ponieważ wszystkie prawdziwe nazwy plików będą ścieżkami bezwzględnymi, możesz łatwo określić, które z nich są wystarczające. Ponadto, jak zauważyli inni, pliki mogą mieć wiele twardych linków wskazujących na nie - spowoduje to tylko zgłoszenie tego, za pomocą którego zostały otwarte. Jeśli chcesz znaleźć wszystkie nazwy dla danego pliku, musisz po prostu przejść przez cały system plików.

bdonlan
źródło
9
Dopóki oryginalny plik nadal zawiera odniesienia do niego (otwarte fdbyłoby takim odniesieniem), numeru i-węzła nie można ponownie wykorzystać. Każde oprogramowanie korzystające z numeru i-węzła po zamknięciu pliku lub przed jego otwarciem podlega z natury warunkom wyścigu.
R .. GitHub PRZESTAŃ POMÓC W LODZIE
3
Niebezpieczeństwo, Will Robinson! To nie zawsze działa - jeśli robisz setuid()sztuczki, /proc/self/fdproces może nie być dostępny. Zobacz: permalink.gmane.org/gmane.linux.kernel/1302546
David Given
2
@bdonlan: a jeśli / proc nie jest zamontowany?
user2284570
1
@ user2284570, ta odpowiedź dotyczy systemu Linux. Nie wiem, czy NetBSD w ogóle obsługuje procfs - jeśli twój współdzielony host tego nie zapewnia, to prawdopodobnie dlatego, że NetBSD w ogóle go nie obsługuje i zamiast tego używa innego mechanizmu. Możesz zadać kolejne pytanie skupiające się na NetBSD, aby zobaczyć, czy ktoś wie, w jaki sposób NetBSD ujawnia te informacje (możesz również wypróbować odpowiedź zneak poniżej, OS X jest bardziej podobny do BSD niż Linux)
bdonlan
1
@bdonlan: NetBSD obsługuje / proc, ale montowanie go nie jest obowiązkowe. Za każdym razem, gdy wspominałem, odpowiedź brzmiała: „Przełącz się na dostawcę o wyższych kosztach, a otrzymasz / proc”. Więc szukam rozwiązania bezprocentowego.
user2284570
90

Miałem ten problem na Mac OS X. Nie mamy /procwirtualnego systemu plików, więc przyjęte rozwiązanie nie działa.

Zamiast tego mamy F_GETPATHpolecenie fcntl:

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

Aby pobrać plik powiązany z deskryptorem pliku, możesz użyć tego fragmentu:

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

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

Ponieważ nigdy nie pamiętam, gdzie MAXPATHLENjest zdefiniowane, pomyślałem, że PATH_MAXz syslimits będzie dobrze.

zneak
źródło
@uchuugaka, prawdopodobnie nie. Użyj getsockname.
zneak
2
Czego oczekujesz? O ile nie jest to gniazdo UNIX, nie ma skojarzonego z nim pliku.
zneak
2
@uchuugaka Tak, wszystko jest plikiem, ale nie wszystko jest pozycją katalogu z nazwą i lokalizacją w drzewie systemu plików. Plik jest reprezentowany przez i-węzeł, może istnieć bez żadnej pozycji katalogu odnoszącej się do niego.
lgeorget
9
W <sys / param.h>: #define MAXPATHLEN PATH_MAX
geowar
1
Właśnie to przetestowałem i pozostaje poprawne, jeśli plik zostanie przeniesiony i wywołasz go ponownie (co oznacza: otrzymujesz nową ścieżkę do pliku). Jednak nie jest to obsługiwane w systemie Linux (testowane na Ubuntu 14.04 - F_GETPATH ​​nie jest zdefiniowane).
felipou
27

W systemie Windows za pomocą GetFileInformationByHandleEx , przekazując FileNameInfo , możesz pobrać nazwę pliku.

Martin przeciwko Löwis
źródło
17
tak bardzo jak nienawidzę okien, zawsze miło jest mieć ich odpowiednik
Matt Joiner
15

Jak podkreśla Tyler, nie ma sposobu, aby zrobić to, czego potrzebujesz "bezpośrednio i niezawodnie", ponieważ dana FD może odpowiadać 0 nazwom plików (w różnych przypadkach) lub> 1 (wiele "twardych linków" jest ogólnie opisywana tą drugą sytuacją ). Jeśli nadal potrzebujesz funkcji ze wszystkimi ograniczeniami (dotyczącymi prędkości ORAZ możliwości uzyskania 0, 2, ... wyników zamiast 1), oto jak możesz to zrobić: po pierwsze, fstat FD - to ci mówi , w rezultacie struct stat, na jakim urządzeniu znajduje się plik, ile ma twardych linków, czy jest to plik specjalny itp. To może już odpowiedzieć na twoje pytanie - np. jeśli 0 twardych linków WIESZ, że w rzeczywistości nie ma odpowiedniej nazwy pliku na dysku.

Jeśli statystyki dają ci nadzieję, musisz "chodzić po drzewie" katalogów na odpowiednim urządzeniu, aż znajdziesz wszystkie twarde linki (lub tylko pierwszy, jeśli nie potrzebujesz więcej niż jednego i każdy to zrobi ). W tym celu używasz readdir (i oczywiście opendir & c) rekurencyjnie otwierających podkatalogi, dopóki nie znajdziesz w struct direntotrzymanym w ten sposób tym samym numerze i-węzła, który miałeś w oryginale struct stat(w tym czasie, jeśli chcesz całą ścieżkę, a nie tylko nazwę, aby odtworzyć łańcuch katalogów, musisz przejść wstecz).

Jeśli to ogólne podejście jest do zaakceptowania, ale potrzebujesz bardziej szczegółowego kodu w C, daj nam znać, nie będzie to trudne do napisania (chociaż wolałbym nie pisać, jeśli jest bezużyteczne, tj. Nie możesz wytrzymać nieuchronnie powolnej wydajności lub możliwość uzyskania! = 1 wyniku na potrzeby Twojej aplikacji ;-).

Alex Martelli
źródło
9

Zanim uznasz to za niemożliwe, proponuję przyjrzeć się kodowi źródłowemu polecenia lsof .

Mogą istnieć ograniczenia, ale wydaje się, że lsof jest w stanie określić deskryptor pliku i nazwę pliku. Ta informacja istnieje w systemie plików / proc, więc powinno być możliwe uzyskanie do niej dostępu z twojego programu.

kaczka
źródło
6

Możesz użyć fstat (), aby uzyskać i-węzeł pliku przez statct stat. Następnie za pomocą readdir () możesz porównać znaleziony i-węzeł z tymi, które istnieją (struct dirent) w katalogu (zakładając, że znasz katalog, w przeciwnym razie będziesz musiał przeszukać cały system plików) i znaleźć odpowiadającą mu nazwę pliku. Paskudny?

PetrosB
źródło
2

Niemożliwy. Deskryptor pliku może mieć wiele nazw w systemie plików lub może nie mieć żadnej nazwy.

Edycja: Zakładając, że mówisz o zwykłym starym systemie POSIX, bez żadnych interfejsów API specyficznych dla systemu operacyjnego, ponieważ nie określiłeś systemu operacyjnego.

Tyler McHenry
źródło
4
wtedy moja odpowiedź ma zastosowanie. Linux nie ma do tego możliwości. Deskryptory plików systemu Linux (POSIX) niekoniecznie odnoszą się do plików, a nawet jeśli odnoszą się do i-węzłów, a nie nazw plików. Deskryptor może wskazywać na usunięty plik (który w związku z tym nie ma nazwy, jest to powszechny sposób tworzenia plików tymczasowych) lub może wskazywać na i-węzeł o wielu nazwach (dowiązania twarde).
Tyler McHenry
3
Spróbuj spojrzeć na kod źródłowy lsof. :) Tak właśnie zrobiłem, kiedy miałem to samo pytanie jakiś czas temu. lsof działa na czarną magię i kozły ofiarne - nie można liczyć na powielenie jego zachowania. Mówiąc dokładniej, lsof jest ściśle powiązany z jądrem systemu Linux i nie robi tego, co robi za pomocą żadnego interfejsu API dostępnego dla kodu użytkownika.
Tyler McHenry
27
Linux ma do tego nieprzenośny interfejs API proc. Rzeczywiście istnieją ograniczenia, ale stwierdzenie, że to niemożliwe, jest po prostu fałszywe.
bdonlan
1
@Tyler - lsof działa w przestrzeni użytkownika. W związku z tym istnieje API do wszystkiego, co robi, dostępne dla kodu
obszaru
1
@Duck, przenośność jest prawdopodobnie powodem, dla którego źródło lsof ma tak dużo czarnej magii; każdy wariant UNIX robi to inaczej. Interfejsy proc w Linuksie nie są takie złe, naprawdę, ale są one dość skąpo udokumentowane.
bdonlan