Отримайте ім'я файлу з дескриптора файлу на C


105

Чи можливо отримати назву дескриптора файлу (Linux) в C?


Я думаю, обрану відповідь слід дати zneak, оскільки його рішення має кращу портативність і не має помічених проблем із доступом.
Сергій

Він не підтримується на Ubuntu 14.04 (ядро 3.16.0-76-generic). Я здогадуюсь, що він взагалі не підтримується в Linux.
felipou

Про macOS дивіться цю відповідь на інше запитання Д. Натанаена .
Джонатан Леффлер

Відповіді:


120

Ви можете використовувати readlinkна /proc/self/fd/NNNде NNN є дескриптор файлу. Це дасть вам ім'я файлу таким, яким воно було, коли він був відкритий - однак, якщо файл був переміщений чи видалений з того часу, він більше не може бути точним (хоча Linux може відстежувати перейменування в деяких випадках). Щоб перевірити, statім'я файлу , задане і fstatФД у вас є, і переконайтеся , що st_devі st_inoті ж.

Звичайно, не всі дескриптори файлів посилаються на файли, і для тих, хто побачить деякі незвичайні текстові рядки, такі як pipe:[1538488]. Оскільки всі справжні імена файлів будуть абсолютними шляхами, ви можете визначити, які з них досить легко. Далі, як зазначають інші, файли можуть мати декілька жорстких посилань, що вказують на них - про це повідомлятиметься лише той, з яким він був відкритий. Якщо ви хочете знайти всі імена для даного файлу, вам просто доведеться пройти всю файлову систему.


9
Поки оригінальний файл все ще має посилання на нього (відкритим fdбуде така посилання), номер введення не може бути використаний повторно. Будь-яке програмне забезпечення, яке використовує номер inode після закриття файлу або перед його відкриттям, по суті є предметом перегонів.
R .. GitHub СТОП ДОПОМОГАЙТЕ

3
Небезпека, Віл Робінзон! Це не завжди працює --- якщо ви робите setuid()фокуси, можливо /proc/self/fd, ваш процес не може бути доступним. Дивіться: permalink.gmane.org/gmane.linux.kernel/1302546
David Дано

2
@bdonlan: а у випадку / proc не встановлений?
користувач2284570

1
@ user2284570, ця відповідь стосується Linux. Я не знаю, чи підтримує NetBSD протоколи взагалі - якщо ваш спільний хост не надає його, можливо, тому, що NetBSD його взагалі не підтримує, а замість цього використовується інший механізм. Ви можете поставити ще одне запитання з фокусом на NetBSD, щоб дізнатися, чи хтось знає, як NetBSD викриває цю інформацію (ви можете також спробувати відповідь zneak нижче, OS X більше схожий на BSD, ніж Linux)
bdonlan

1
@bdonlan: підтримка / proc NetBSD, але це не обов'язково. Кожного разу, коли я згадував, відповідь ставала "переходьте до більш високого постачальника витрат, і ви отримаєте / придбаєте". Тому я шукаю безпроблемне рішення.
користувач2284570

90

У мене була проблема в Mac OS X. У нас немає /procвіртуальної файлової системи, тому прийняте рішення не може працювати.

Ми, замість того , є F_GETPATHкоманди для fcntl:

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

Отже, щоб отримати файл, пов’язаний з дескриптором файлів, ви можете використовувати цей фрагмент:

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

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

Оскільки я ніколи не пам’ятаю, де MAXPATHLENвизначено, я думав, що PATH_MAXз syslimits це буде добре.


@uchuugaka, певно, ні. Використовуйте getsockname.
zneak

2
Що ти очікуєш? Якщо це не сокет UNIX, у нього не пов’язано жодного файлу.
zneak

2
@uchuugaka Так, все - це файл, але не все - це запис каталогу з іменем та розташуванням всередині дерева файлової системи. Файл представлений inode, він може існувати без будь-якого запису каталогів до нього.
lgeorget

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

1
Я щойно це перевірив, і це залишається правильним, якщо файл переміщено, і ви його знову зателефонуєте (це означає: ви отримаєте новий шлях до файлу). Однак це не підтримується в Linux (тестується на Ubuntu 14.04 - F_GETPATH ​​не визначено).
felipou


15

Як вказує Тайлер, немає ніякого способу зробити те, що вам потрібно "безпосередньо та надійно", оскільки даний FD може відповідати 0 назви файлів (у різних випадках) або> 1 (декілька "жорстких посилань" - як описується остання ситуація. ). Якщо вам все-таки потрібна функціональність з усіма обмеженнями (щодо швидкості ТА щодо можливості отримання 0, 2, ... результатів, а не 1), ось як це зробити: по-перше, fstat FD - це говорить вам , в результаті чого struct stat, на якому пристрої працює файл, скільки жорстких посилань у ньому є, чи це спеціальний файл тощо. Це вже може відповісти на ваше запитання - наприклад, якщо 0 жорстких посилань ви ЗНАЄМО, насправді немає відповідного імені файлу на диску.

Якщо статистика дає вам надію, вам доведеться "обходити дерево" каталогів на відповідному пристрої, поки ви не знайдете всі жорсткі посилання (або лише перше, якщо вам не потрібно більше одного, і будь-який зробить) ). З цією метою ви використовуєте readdir (і, звичайно, opendir & c), рекурсивно відкриваючи підкаталоги, доки не знайдете struct direntтаким чином такий самий номер inode, який ви мали в оригіналі struct stat(в цей час, якщо ви хочете весь шлях, а не лише ім'я, вам потрібно буде пройти ланцюжок каталогів назад, щоб відновити його).

Якщо цей загальний підхід прийнятний, але вам потрібен більш детальний код C, повідомте нас, це буде важко написати (хоча я краще не напишу його, якщо він марний, тобто ви не зможете протистояти неминуче повільній продуктивності або можливість отримати! = 1 результат для вашої заявки ;-).


9

Перш ніж списати це як неможливе, пропоную переглянути вихідний код команди lsof .

Можуть бути обмеження, але lsof здається здатним визначати дескриптор файлу та ім'я файлу. Ця інформація існує у файловій системі / proc, тому її можна отримати за допомогою програми.


6

Ви можете використовувати fstat (), щоб отримати inode файлу за допомогою stat stat. Потім, використовуючи readdir (), ви можете порівняти знайдений інод з тими, які існують (struct dirent) у каталозі (якщо припустити, що ви знаєте каталог, інакше вам доведеться шукати всю файлову систему) і знайти відповідне ім'я файлу. Неприємний?


2

Неможливо. Дескриптор файлу може мати декілька імен у файловій системі або взагалі не мати імені.

Редагувати: якщо припустити, що ви говорите про звичайну стару систему POSIX, без будь-яких API для ОС, оскільки ви не вказали ОС.


4
то стосується моєї відповіді. У Linux немає засобів для цього. Дескриптори файлів Linux (POSIX) не обов'язково посилаються на файли, і навіть якщо вони посилаються на узори, а не назви файлів. Дескриптор може вказувати на видалений файл (який, отже, не має імені, це звичайний спосіб створення тимчасових файлів) або може вказувати на індею з кількома іменами (жорсткі посилання).
Тайлер Макенрі

3
Спробуйте подивитися вихідний код lsof. :) Ось що я і зробив, коли у мене було те саме запитання деякий час назад. lsof працює над чорною магією та жертовними козами - ви не можете сподіватися дублювати її поведінку. Щоб бути більш конкретним, lsof тісно поєднаний з ядром Linux, і не робить того, що робить за допомогою будь-якого API, доступного для коду user-land.
Тайлер Макенрі

27
Linux має для цього портативний API проц. Дійсно є обмеження, але сказати, що це неможливо, просто помилково.
бдонлан

1
@Tyler - lsof працює в просторі користувачів. Тому є API для всього, що він доступний для коду
userland

1
@Duck, портативність там, ймовірно, в джерелі lsof має стільки чорної магії; кожен варіант UNIX робить це по-різному. Інтерфейси linux proc не дуже погані, але алебіт досить рідко документований.
bdonlan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.