Récupérer le nom de fichier du descripteur de fichier en C

105

Est-il possible d'obtenir le nom de fichier d'un descripteur de fichier (Linux) en C?

adk
la source
Je suppose que la réponse choisie devrait être donnée à zneak car sa solution a une meilleure portabilité et n'a pas de problèmes d'accès notés.
Sergei
Il n'est pas pris en charge sur Ubuntu 14.04 (noyau 3.16.0-76-generic). Je suppose que ce n'est pas du tout pris en charge sous Linux.
felipou
Pour macOS, consultez cette réponse à une autre question de D.Nathanael .
Jonathan Leffler

Réponses:

120

Vous pouvez utiliser readlinkle /proc/self/fd/NNNNNN est le descripteur de fichier. Cela vous donnera le nom du fichier tel qu'il était quand il a été ouvert - cependant, si le fichier a été déplacé ou supprimé depuis, il peut ne plus être exact (bien que Linux puisse suivre les renommés dans certains cas). Pour vérifier, statle nom du fichier donné et fstatla vous avez fd, et assurez - vous st_devet st_inosont les mêmes.

Bien sûr, tous les descripteurs de fichiers ne font pas référence à des fichiers, et pour ceux-ci, vous verrez des chaînes de texte étranges, telles que pipe:[1538488]. Puisque tous les noms de fichiers réels seront des chemins absolus, vous pouvez déterminer lesquels sont assez facilement. De plus, comme d'autres l'ont noté, les fichiers peuvent avoir plusieurs liens physiques pointant vers eux - cela ne rapportera que celui avec lequel ils ont été ouverts. Si vous voulez trouver tous les noms d'un fichier donné, il vous suffira de parcourir tout le système de fichiers.

bdonlan
la source
9
Tant que le fichier d'origine a encore des références (une ouverture fdserait une telle référence), le numéro d'inode ne peut pas être réutilisé. Tout logiciel utilisant un numéro d'inode après avoir fermé le fichier ou avant de l'ouvrir est intrinsèquement soumis à des conditions de concurrence.
R .. GitHub STOP HELPING ICE
3
Danger, Will Robinson! Cela ne fonctionne pas toujours --- si vous faites des setuid()astuces, il est possible que /proc/self/fdvotre processus ne soit pas accessible. Voir: permalink.gmane.org/gmane.linux.kernel/1302546
David donné
2
@bdonlan: et dans le cas où / proc n'est pas monté?
user2284570
1
@ user2284570, cette réponse est spécifique à Linux. Je ne sais pas si NetBSD prend en charge procfs du tout - si votre hôte partagé ne le fournit pas, c'est probablement parce que NetBSD ne le prend pas du tout en charge et utilise un autre mécanisme à la place. Vous voudrez peut-être poster une autre question avec un focus NetBSD pour voir si quelqu'un sait comment NetBSD expose ces informations (vous pouvez également essayer la réponse de zneak ci-dessous, OS X est plus similaire à BSD que Linux)
bdonlan
1
@bdonlan: supporte NetBSD / proc mais il n'est pas obligatoire de le monter. Chaque fois que je l'ai mentionné, la réponse est devenue "passez à un fournisseur plus coûteux et vous obtiendrez / proc". Je recherche donc une solution sans processus.
user2284570
90

J'ai eu ce problème sur Mac OS X. Nous n'avons pas de /procsystème de fichiers virtuel, donc la solution acceptée ne peut pas fonctionner.

Nous avons, à la place, une F_GETPATHcommande pour fcntl:

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

Donc, pour obtenir le fichier associé à un descripteur de fichier, vous pouvez utiliser cet extrait de code:

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

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

Comme je ne me souviens jamais où MAXPATHLENest défini, j'ai pensé que les PATH_MAXlimites système seraient bien.

zneak
la source
@uchuugaka, probablement pas. Utilisez getsockname.
zneak
2
Qu'attendez-vous? Sauf s'il s'agit d'un socket UNIX, aucun fichier n'est associé.
zneak
2
@uchuugaka Oui, tout est un fichier, mais tout n'est pas une entrée de répertoire avec un nom et un emplacement dans l'arborescence du système de fichiers. Un fichier est représenté par un inode, il peut exister sans aucune entrée de répertoire s'y référant.
lgeorget
9
Dans <sys / param.h>: #define MAXPATHLEN PATH_MAX
geowar
1
Je viens de tester cela et cela reste correct si le fichier est déplacé et que vous l'appelez à nouveau (ce qui signifie: vous obtenez le nouveau chemin du fichier). Cependant, cela n'est pas pris en charge sous Linux (testé sur Ubuntu 14.04 - F_GETPATH ​​n'est pas défini).
felipou
27

Dans Windows, avec GetFileInformationByHandleEx , en passant FileNameInfo , vous pouvez récupérer le nom du fichier.

Martin c.Löwis
la source
17
autant que je déteste les fenêtres, l'équivalent de Windows est toujours agréable d'avoir
Matt Joiner
15

Comme le souligne Tyler, il n'y a aucun moyen de faire ce dont vous avez besoin "directement et de manière fiable", car un FD donné peut correspondre à 0 noms de fichiers (dans divers cas) ou> 1 (plusieurs "liens durs" est la façon dont cette dernière situation est généralement décrite ). Si vous avez encore besoin de la fonctionnalité avec toutes les limitations (sur la vitesse ET sur la possibilité d'obtenir 0, 2, ... résultats plutôt que 1), voici comment vous pouvez le faire: d'abord, fstat le FD - cela vous dit , dans le résultat struct stat, sur quel périphérique le fichier vit, combien de liens durs il a, s'il s'agit d'un fichier spécial, etc. Cela peut déjà répondre à votre question - par exemple, si 0 lien dur, vous SAVEZ qu'il n'y a en fait pas de nom de fichier correspondant sur disque.

Si les statistiques vous donnent de l'espoir, alors vous devez «parcourir l'arborescence» des répertoires sur l'appareil concerné jusqu'à ce que vous trouviez tous les liens en dur (ou juste le premier, si vous n'en avez pas besoin de plus d'un et n'importe lequel le fera. ). Pour cela, vous utilisez readdir (et opendir & c bien sûr) en ouvrant récursivement les sous-répertoires jusqu'à ce que vous trouviez dans un struct direntainsi reçu le même numéro d'inode que vous aviez dans l'original struct stat(à quel moment si vous voulez le chemin complet, plutôt que juste le nom, vous devrez parcourir la chaîne de répertoires en arrière pour la reconstruire).

Si cette approche générale est acceptable, mais que vous avez besoin d'un code C plus détaillé, faites-le nous savoir, ce ne sera pas difficile à écrire (bien que je préfère ne pas l'écrire si c'est inutile, c'est-à-dire que vous ne pouvez pas supporter les performances inévitablement lentes ou le possibilité d'obtenir! = 1 résultat pour les besoins de votre candidature ;-).

Alex Martelli
la source
9

Avant d'écrire cela comme impossible, je vous suggère de regarder le code source de la commande lsof .

Il peut y avoir des restrictions mais lsof semble capable de déterminer le descripteur et le nom du fichier. Ces informations existent dans le système de fichiers / proc, il devrait donc être possible d'accéder à partir de votre programme.

canard
la source
6

Vous pouvez utiliser fstat () pour obtenir l'inode du fichier par struct stat. Ensuite, en utilisant readdir (), vous pouvez comparer l'inode que vous avez trouvé avec ceux qui existent (struct dirent) dans un répertoire (en supposant que vous connaissez le répertoire, sinon vous devrez rechercher tout le système de fichiers) et trouver le nom de fichier correspondant. Méchant?

PetrosB
la source
2

Impossible. Un descripteur de fichier peut avoir plusieurs noms dans le système de fichiers ou ne pas avoir de nom du tout.

Edit: En supposant que vous parlez d'un ancien système POSIX, sans API spécifiques au système d'exploitation, puisque vous n'avez pas spécifié de système d'exploitation.

Tyler McHenry
la source
4
alors ma réponse s'applique. Linux n'a aucune installation pour faire cela. Les descripteurs de fichiers Linux (POSIX) ne font pas nécessairement référence à des fichiers, et même s'ils le font, ils font référence à des inodes, pas à des noms de fichiers. Un descripteur peut pointer vers un fichier supprimé (qui n'a donc pas de nom, c'est une manière courante de créer des fichiers temporaires) ou il peut pointer vers un inode avec plusieurs noms (liens physiques).
Tyler McHenry
3
Essayez de jeter un œil au code source de lsof. :) C'est ce que j'ai fait quand j'ai moi-même posé la même question il y a quelque temps. lsof fonctionne sur la magie noire et les chèvres sacrificielles - vous ne pouvez pas espérer dupliquer son comportement. Pour être plus précis, lsof est étroitement couplé au noyau Linux et ne fait pas ce qu'il fait au moyen d'une API disponible pour le code utilisateur.
Tyler McHenry
27
Linux a une API proc non portable pour cela. Il y a effectivement des limites, mais dire que c'est impossible est tout simplement faux.
bdonlan
1
@Tyler - lsof s'exécute dans l'espace utilisateur. Par conséquent, il existe une API pour tout ce qu'elle fait disponible pour le code
userland
1
@Duck, la portabilité est probablement la raison pour laquelle la source de lsof a tant de magie noire; chaque variante UNIX le fait différemment. Les interfaces Linux proc ne sont pas trop mauvaises, vraiment, alebit plutôt peu documentées.
bdonlan le