Rufen Sie den Dateinamen aus dem Dateideskriptor in C ab


105

Ist es möglich, den Dateinamen eines Dateideskriptors (Linux) in C abzurufen?


Ich denke, die gewählte Antwort sollte zneak gegeben werden, da seine Lösung eine bessere Portabilität aufweist und keine bekannten Zugriffsprobleme aufweist.
Sergei

Es wird unter Ubuntu 14.04 (Kernel 3.16.0-76-generic) nicht unterstützt. Ich vermute, es wird unter Linux überhaupt nicht unterstützt.
Felipou

Siehe für macOS diese Antwort auf eine andere Frage von D.Nathanael .
Jonathan Leffler

Antworten:


120

Sie können mit readlinkauf /proc/self/fd/NNNdem NNN ist der Dateideskriptor. Dadurch erhalten Sie den Namen der Datei wie beim Öffnen. Wenn die Datei jedoch seitdem verschoben oder gelöscht wurde, ist sie möglicherweise nicht mehr korrekt (obwohl Linux in einigen Fällen Umbenennungen verfolgen kann). Um zu überprüfen, statder Dateiname angegeben und fstatdie fd Sie haben, und stellen Sie sicher , st_devund st_inosind gleich.

Natürlich beziehen sich nicht alle Dateideskriptoren auf Dateien, und für diese werden einige seltsame Textzeichenfolgen angezeigt, z pipe:[1538488]. Da alle realen Dateinamen absolute Pfade sind, können Sie leicht bestimmen, welche diese Pfade sind. Wie andere angemerkt haben, können Dateien mehrere Hardlinks haben, die auf sie verweisen - dies meldet nur den, mit dem sie geöffnet wurden. Wenn Sie alle Namen für eine bestimmte Datei finden möchten, müssen Sie nur das gesamte Dateisystem durchlaufen.


9
Solange die Originaldatei noch Verweise darauf enthält (ein Öffnen fdwäre ein solcher Verweis), kann die Inode-Nummer nicht wiederverwendet werden. Jede Software, die nach dem Schließen der Datei oder vor dem Öffnen eine Inode-Nummer verwendet, unterliegt von Natur aus den Rennbedingungen.
R .. GitHub STOP HELPING ICE

3
Gefahr, Will Robinson! Dies funktioniert nicht immer - wenn Sie setuid()Tricks ausführen, kann es sein /proc/self/fd, dass Ihr Prozess nicht auf Sie zugreift. Siehe: permalink.gmane.org/gmane.linux.kernel/1302546
David Given

2
@bdonlan: und in dem fall / proc ist nicht gemountet?
user2284570

1
@ user2284570, diese Antwort ist Linux-spezifisch. Ich weiß nicht, ob NetBSD procfs überhaupt unterstützt. Wenn Ihr freigegebener Host es nicht bereitstellt, liegt dies wahrscheinlich daran, dass NetBSD es überhaupt nicht unterstützt und stattdessen einen anderen Mechanismus verwendet. Vielleicht möchten Sie eine weitere Frage mit einem NetBSD-Fokus stellen, um zu sehen, ob jemand weiß, wie NetBSD diese Informationen verfügbar macht (Sie können auch die Antwort von zneak unten ausprobieren, OS X ist BSD ähnlicher als Linux)
bdonlan

1
@bdonlan: NetBSD support / proc, aber es ist nicht obligatorisch, es zu mounten. Jedes Mal, wenn ich es erwähnte, lautete die Antwort "Wechseln Sie zu einem Anbieter mit höheren Kosten und Sie erhalten / proc". Also suche ich nach einer prozesslosen Lösung.
user2284570

90

Ich hatte dieses Problem unter Mac OS X. Wir haben kein /procvirtuelles Dateisystem, daher kann die akzeptierte Lösung nicht funktionieren.

Wir haben stattdessen einen F_GETPATHBefehl für fcntl:

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

Um die einem Dateideskriptor zugeordnete Datei abzurufen, können Sie dieses Snippet verwenden:

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

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

Da ich mich nie daran erinnere, wo MAXPATHLENdefiniert ist, dachte ich, dass PATH_MAXSyslimits in Ordnung wären.


@uchuugaka, wahrscheinlich nicht. Verwenden Sie getsockname.
Zneak

2
Was erwartest du? Sofern es sich nicht um einen UNIX-Socket handelt, ist keine Datei zugeordnet.
Zneak

2
@uchuugaka Ja, alles ist eine Datei, aber nicht alles ist ein Verzeichniseintrag mit einem Namen und einem Speicherort im Dateisystembaum. Eine Datei wird durch einen Inode dargestellt. Sie kann existieren, ohne dass ein Verzeichniseintrag darauf verweist.
Lgeorget

9
In <sys / param.h>: #define MAXPATHLEN PATH_MAX
geowar

1
Ich habe dies gerade getestet und es bleibt korrekt, wenn die Datei verschoben wird und Sie sie erneut aufrufen (was bedeutet: Sie erhalten den neuen Pfad der Datei). Dies wird jedoch unter Linux nicht unterstützt (getestet unter Ubuntu 14.04 - F_GETPATH ​​ist nicht definiert).
Felipou


15

Wie Tyler betont, gibt es keine Möglichkeit, "direkt und zuverlässig" das zu tun, was Sie benötigen, da eine bestimmte FD 0 Dateinamen (in verschiedenen Fällen) oder> 1 (mehrere "harte Links") entsprechen kann, wie die letztere Situation allgemein beschrieben wird ). Wenn Sie die Funktionalität mit allen Einschränkungen (Geschwindigkeit UND Möglichkeit, 0, 2, ... Ergebnisse anstelle von 1 zu erhalten) weiterhin benötigen, können Sie dies folgendermaßen tun: Zuerst fstat the FD - dies sagt Ihnen In der Folge struct stat, auf welchem ​​Gerät die Datei lebt, wie viele Hardlinks sie hat, ob es sich um eine spezielle Datei handelt usw. Dies kann Ihre Frage bereits beantworten - z. B. wenn Sie 0 Hardlinks kennen, wissen Sie, dass es tatsächlich keinen entsprechenden Dateinamen gibt auf der Festplatte.

Wenn die Statistiken Ihnen Hoffnung geben, müssen Sie den Baum der Verzeichnisse auf dem entsprechenden Gerät "durchgehen", bis Sie alle festen Links gefunden haben (oder nur den ersten, wenn Sie nicht mehr als einen benötigen und jeder dies tun wird ). Zu diesem Zweck verwenden Sie readdir (und natürlich opendir & c), um Unterverzeichnisse rekursiv zu öffnen, bis Sie in einem struct direntso empfangenen die gleiche Inode-Nummer finden, die Sie im Original hatten struct stat(zu diesem Zeitpunkt, wenn Sie den gesamten Pfad und nicht nur den Namen möchten). Sie müssen die Verzeichniskette rückwärts durchlaufen, um sie zu rekonstruieren.

Wenn dieser allgemeine Ansatz akzeptabel ist, Sie jedoch detaillierteren C-Code benötigen, lassen Sie es uns wissen, es wird nicht schwer zu schreiben sein (obwohl ich ihn lieber nicht schreiben würde, wenn er nutzlos ist, dh Sie können der unvermeidlich langsamen Leistung oder der Leistung nicht standhalten Möglichkeit zu bekommen! = 1 Ergebnis für die Zwecke Ihrer Bewerbung ;-).


9

Bevor Sie dies als unmöglich abschreiben, sollten Sie sich den Quellcode des Befehls lsof ansehen .

Es kann Einschränkungen geben, aber lsof scheint in der Lage zu sein, den Dateideskriptor und den Dateinamen zu bestimmen. Diese Informationen sind im Dateisystem / proc vorhanden, sodass es möglich sein sollte, von Ihrem Programm aus darauf zuzugreifen.


6

Sie können fstat () verwenden, um den Inode der Datei nach struct stat abzurufen. Mit readdir () können Sie dann den gefundenen Inode mit dem in einem Verzeichnis vorhandenen (struct dirent) vergleichen (vorausgesetzt, Sie kennen das Verzeichnis, andernfalls müssen Sie das gesamte Dateisystem durchsuchen) und den entsprechenden Dateinamen finden. Böse?


2

Unmöglich. Ein Dateideskriptor kann mehrere Namen im Dateisystem haben oder überhaupt keinen Namen.

Bearbeiten: Angenommen, Sie sprechen von einem einfachen alten POSIX-System ohne betriebssystemspezifische APIs, da Sie kein Betriebssystem angegeben haben.


4
dann gilt meine Antwort. Linux hat keine Möglichkeiten, dies zu tun. Linux (POSIX) -Dateideskriptoren beziehen sich nicht unbedingt auf Dateien, und selbst wenn, beziehen sie sich auf Inodes, nicht auf Dateinamen. Ein Deskriptor kann auf eine gelöschte Datei verweisen (die daher keinen Namen hat, dies ist eine übliche Methode zum Erstellen von temporären Dateien), oder auf einen Inode mit mehreren Namen (Hardlinks).
Tyler McHenry

3
Schauen Sie sich den lsof-Quellcode an. :) Das habe ich getan, als ich vor einiger Zeit selbst dieselbe Frage hatte. lsof arbeitet an schwarzer Magie und Opferziegen - man kann nicht hoffen, sein Verhalten zu duplizieren. Um genauer zu sein, ist lsof eng mit dem Linux-Kernel verbunden und macht nicht das, was es macht, mittels einer API, die für User-Land-Code verfügbar ist.
Tyler McHenry

27
Linux hat hierfür eine nicht portable Proc-API. Es gibt zwar Einschränkungen, aber zu sagen, dass es unmöglich ist, ist einfach falsch.
Bdonlan

1
@ Tyler - lsof wird im Userspace ausgeführt. Daher gibt es eine API für alles, was es für Userland-Code zur Verfügung stellt :)
bdonlan

1
@Duck, die Portabilität dort ist wahrscheinlich der Grund, warum die Quelle von lsof so viel schwarze Magie hat; Jede UNIX-Variante macht es anders. Die Linux Proc-Schnittstellen sind eigentlich gar nicht so schlecht, Alebit eher spärlich dokumentiert.
Bdonlan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.