Recupera il nome del file dal descrittore di file in C.


105

È possibile ottenere il nome del file di un descrittore di file (Linux) in C?


Immagino che la risposta scelta dovrebbe essere data a zneak poiché la sua soluzione ha una migliore portabilità e non ha problemi di accesso noti.
Sergei

Non è supportato su Ubuntu 14.04 (kernel 3.16.0-76-generico). Immagino che non sia affatto supportato su Linux.
felipou

Per macOS, vedi questa risposta a un'altra domanda di D.Nathanael .
Jonathan Leffler

Risposte:


120

È possibile utilizzare readlinksu /proc/self/fd/NNNdove NNN è il descrittore di file. Questo ti darà il nome del file com'era quando è stato aperto - tuttavia, se il file è stato spostato o cancellato da allora, potrebbe non essere più accurato (sebbene Linux possa tenere traccia delle rinomine in alcuni casi). Per verificare, statil nome del file dato e fstatil fd che hai, e assicurati che st_deve st_inosiano gli stessi.

Ovviamente, non tutti i descrittori di file si riferiscono a file e per quelli vedrai alcune strane stringhe di testo, come pipe:[1538488]. Poiché tutti i nomi di file reali saranno percorsi assoluti, è possibile determinare quali sono abbastanza facilmente. Inoltre, come altri hanno notato, i file possono avere più collegamenti fisici che puntano a loro: questo riporterà solo quello con cui è stato aperto. Se vuoi trovare tutti i nomi per un dato file, dovrai solo attraversare l'intero filesystem.


9
Finché il file originale ha ancora riferimenti ad esso (un'apertura fdsarebbe un tale riferimento), il numero di inode non può essere riutilizzato. Qualsiasi software che utilizza un numero di inode dopo aver chiuso il file o prima di aprirlo è intrinsecamente soggetto a condizioni di competizione.
R .. GitHub SMETTA DI AIUTARE IL GHIACCIO

3
Pericolo, Will Robinson! Questo non sempre funziona --- se fai setuid()trucchi, è possibile /proc/self/fdche non sia accessibile dal tuo processo. Vedi: permalink.gmane.org/gmane.linux.kernel/1302546
David Given

2
@bdonlan: e nel caso / proc non è montato?
user2284570

1
@ user2284570, questa risposta è specifica di Linux. Non so se NetBSD supporti procfs - se il tuo host condiviso non lo fornisce, probabilmente è perché NetBSD non lo supporta affatto e utilizza invece un altro meccanismo. Potresti voler pubblicare un'altra domanda con un focus su NetBSD per vedere se qualcuno sa come NetBSD espone queste informazioni (potresti anche provare la risposta di zneak di seguito, OS X è più simile a BSD che a Linux)
bdonlan

1
@bdonlan: NetBSD supporta / proc ma non è obbligatorio montarlo. Ogni volta che ho menzionato, la risposta è stata "passa a un fornitore di costo più elevato e otterrai / proc". Quindi sto cercando una soluzione senza processo.
user2284570

90

Ho avuto questo problema su Mac OS X. Non abbiamo un /procfile system virtuale, quindi la soluzione accettata non può funzionare.

Abbiamo, invece, un F_GETPATHcomando per fcntl:

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

Quindi, per ottenere il file associato a un descrittore di file, puoi usare questo frammento:

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

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

Dato che non ricordo mai dove MAXPATHLENè definito, ho pensato che PATH_MAXda syslimits sarebbe andato bene.


@uchuugaka, probabilmente no. Usa getsockname.
zneak

2
Cosa ti aspetti? A meno che non sia un socket UNIX, non ha alcun file associato.
zneak

2
@uchuugaka Sì, tutto è un file, ma non tutto è una voce di directory con un nome e una posizione all'interno dell'albero del filesystem. Un file è rappresentato da un inode, può esistere senza alcuna voce di directory che faccia riferimento ad esso.
lgeorget

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

1
L'ho appena testato e rimane corretto se il file viene spostato e lo chiami di nuovo (ovvero: ottieni il nuovo percorso del file). Tuttavia questo non è supportato su Linux (testato su Ubuntu 14.04 - F_GETPATH ​​non è definito).
felipou


15

Come fa notare Tyler, non c'è modo di fare ciò che si richiede "direttamente e in modo affidabile", poiché un dato FD può corrispondere a 0 nomi di file (in vari casi) o> 1 (più "collegamenti fisici" è come viene generalmente descritta l'ultima situazione ). Se hai ancora bisogno della funzionalità con tutte le limitazioni (sulla velocità E sulla possibilità di ottenere 0, 2, ... risultati invece di 1), ecco come puoi farlo: primo, fstat FD - questo ti dice , nel risultato struct stat, su quale dispositivo risiede il file, quanti collegamenti fisici ha, se si tratta di un file speciale, ecc. Questo potrebbe già rispondere alla tua domanda - ad es. se 0 collegamenti fisici SAPRETE che in realtà non esiste un nome file corrispondente su disco.

Se le statistiche ti danno speranza, allora devi "percorrere l'albero" delle directory sul dispositivo pertinente finché non trovi tutti i collegamenti fisici (o solo il primo, se non ne hai bisogno più di uno e nessuno lo farà ). A tale scopo, usi readdir (e opendir & c ovviamente) aprendo ricorsivamente le sottodirectory finché non trovi in ​​un struct direntcosì ricevuto lo stesso numero di inode che avevi nell'originale struct stat(in quel momento se vuoi l'intero percorso, piuttosto che solo il nome, dovrai percorrere la catena di directory all'indietro per ricostruirla).

Se questo approccio generale è accettabile, ma hai bisogno di codice C più dettagliato, faccelo sapere, non sarà difficile da scrivere (anche se preferirei non scriverlo se è inutile, cioè non puoi sopportare le prestazioni inevitabilmente lente o il possibilità di ottenere! = 1 risultato ai fini dell'applicazione ;-).


9

Prima di scrivere questo come impossibile ti suggerisco di guardare il codice sorgente del comando lsof .

Potrebbero esserci delle restrizioni ma lsof sembra in grado di determinare il descrittore e il nome del file. Queste informazioni esistono nel filesystem / proc quindi dovrebbe essere possibile ottenerle dal tuo programma.


6

Puoi usare fstat () per ottenere l'inode del file da struct stat. Quindi, usando readdir () puoi confrontare l'inode che hai trovato con quelli che esistono (struct dirent) in una directory (assumendo che tu conosca la directory, altrimenti dovrai cercare in tutto il filesystem) e trovare il nome del file corrispondente. Cattiva?


2

Impossibile. Un descrittore di file può avere più nomi nel filesystem o potrebbe non avere alcun nome.

Modifica: supponendo che tu stia parlando di un semplice vecchio sistema POSIX, senza API specifiche del sistema operativo, poiché non hai specificato un sistema operativo.


4
allora la mia risposta si applica. Linux non ha strutture per farlo. I descrittori di file Linux (POSIX) non si riferiscono necessariamente ai file, e anche se fanno riferimento a inode, non a nomi di file. Un descrittore può puntare a un file cancellato (che quindi non ha nome, questo è un modo comune di creare file temporanei) oppure può puntare a un inode con più nomi (hard link).
Tyler McHenry,

3
Prova a dare un'occhiata al codice sorgente di lsof. :) Questo è quello che ho fatto quando ho avuto la stessa domanda qualche tempo fa. Lsof lavora sulla magia nera e sulle capre sacrificali: non puoi sperare di duplicarne il comportamento. Per essere più precisi, lsof è strettamente collegato al kernel Linux e non fa quello che fa per mezzo di alcuna API disponibile per il codice user-land.
Tyler McHenry

27
Linux ha un'API proc non portabile per questo. Ci sono davvero dei limiti, ma dire che è impossibile è semplicemente falso.
bdonlan,

1
@Tyler - lsof viene eseguito nello spazio utente. Pertanto, esiste un'API per tutto ciò che fa disponibile per il codice userland :)
bdonlan

1
@Duck, la portabilità è probabilmente il motivo per cui il sorgente di lsof ha così tanta magia nera; ogni variante UNIX lo fa in modo diverso. Le interfacce Linux proc non sono poi così male, davvero, anche se sono scarsamente documentate.
bdonlan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.