Получить имя файла из файлового дескриптора в C

105

Можно ли получить имя файла дескриптора файла (Linux) на C?

ADK
источник
Думаю, выбранный ответ следует дать zneak, так как его решение лучше переносимо и не имеет замеченных проблем с доступом.
Сергей
Он не поддерживается в Ubuntu 14.04 (ядро 3.16.0-76-generic). Я предполагаю, что это вообще не поддерживается в Linux.
felipou
Для macOS см. Этот ответ на другой вопрос Д. Натанаэля .
Джонатан Леффлер,

Ответы:

121

Вы можете использовать readlinkна /proc/self/fd/NNNгде NNN является дескриптор файла. Это даст вам имя файла, которое было при его открытии - однако, если файл был перемещен или удален с тех пор, он может быть неточным (хотя Linux может отслеживать переименования в некоторых случаях). Чтобы проверить, statукажите имя файла и fstatимеющийся у вас fd и убедитесь, что st_devи st_inoсовпадают.

Конечно, не все файловые дескрипторы относятся к файлам, и для них вы увидите некоторые странные текстовые строки, например pipe:[1538488]. Поскольку все настоящие имена файлов будут абсолютными путями, вы можете легко определить, какие из них. Кроме того, как отмечали другие, файлы могут иметь несколько жестких ссылок, указывающих на них - это будет сообщать только о той, с которой он был открыт. Если вы хотите найти все имена для данного файла, вам просто нужно пройти через всю файловую систему.

бдонлан
источник
9
Пока исходный файл все еще имеет ссылки на него ( fdтакой ссылкой будет открытый файл ), номер inode не может быть использован повторно. Любое программное обеспечение, использующее номер inode после закрытия файла или перед его открытием, по своей сути подвержено условиям гонки.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE
3
Опасно, Уилл Робинсон! Это не всегда работает - если вы выполняете setuid()трюки, это /proc/self/fdможет быть недоступно для вашего процесса. См .: permalink.gmane.org/gmane.linux.kernel/1302546
Дэвид Гивен
2
@bdonlan: а в случае, если / proc не примонтирован?
user2284570 02
1
@ user2284570, этот ответ зависит от Linux. Я не знаю, поддерживает ли NetBSD вообще procfs - если ваш общий хост не предоставляет его, вероятно, это потому, что NetBSD не поддерживает его вообще и вместо этого использует другой механизм. Возможно, вы захотите опубликовать еще один вопрос с фокусом на NetBSD, чтобы узнать, знает ли кто-нибудь, как NetBSD предоставляет эту информацию (вы можете также попробовать ответ zneak ниже, OS X больше похожа на BSD, чем на Linux)
bdonlan
1
@bdonlan: NetBSD поддерживает / proc, но монтировать его не обязательно. Каждый раз, когда я упоминал, ответ был «переключитесь на более дорогого поставщика, и вы получите / proc». Итак, я ищу решение без процедур.
user2284570
91

У меня была эта проблема в 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 Да, все является файлом, но не все является записью каталога с именем и расположением внутри дерева файловой системы. Файл представлен индексным дескриптором, он может существовать без ссылки на какую-либо запись в каталоге.
lgeorget
9
В <sys / param.h>: #define MAXPATHLEN PATH_MAX
geowar
1
Я только что проверил это, и он остается правильным, если файл перемещен и вы вызываете его снова (что означает: вы получаете новый путь к файлу). Однако это не поддерживается в Linux (проверено в Ubuntu 14.04 - F_GETPATH ​​не определен).
felipou
27

В Windows с GetFileInformationByHandleEx , передавая FileNameInfo , вы можете получить имя файла.

Мартин против Лёвиса
источник
18
Как бы я ни ненавидел окна, всегда приятно иметь их эквивалент
Мэтт Джойнер,
15

Как указывает Тайлер, невозможно сделать то, что вам нужно, «прямо и надежно», поскольку данный FD может соответствовать 0 имен файлов (в различных случаях) или> 1 (несколько «жестких ссылок» - это то, как обычно описывается последняя ситуация. ). Если вам все еще нужна функциональность со всеми ограничениями (по скорости И по возможности получения результатов 0, 2, ..., а не 1), вот как вы можете это сделать: во-первых, fstat FD - это говорит вам , в результате struct stat, на каком устройстве находится файл, сколько жестких ссылок у него есть, является ли он специальным файлом и т. д. Это может уже ответить на ваш вопрос - например, если 0 жестких ссылок, вы ЗНАЕТЕ, что на самом деле нет соответствующего имени файла на диске.

Если статистика вселяет надежду, то вам нужно «пройтись по дереву» каталогов на соответствующем устройстве, пока вы не найдете все жесткие ссылки (или только первую, если вам не нужно больше одной, и подойдет любая ). Для этой цели вы используете readdir (и, конечно, opendir & c), рекурсивно открывая подкаталоги до тех пор, пока вы не найдете в struct direntполученном таким образом том же индексном узле, который был у вас в оригинале struct stat(в этот момент, если вам нужен весь путь, а не только имя, вам нужно будет пройти по цепочке каталогов назад, чтобы восстановить ее).

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

Алекс Мартелли
источник
9

Прежде чем списать это на невозможное, предлагаю вам взглянуть на исходный код команды lsof .

Могут быть ограничения, но lsof, похоже, способен определять дескриптор файла и имя файла. Эта информация существует в файловой системе / proc, поэтому ее можно будет получить из вашей программы.

Утка
источник
6

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

ПетросБ
источник
1

Невозможно. Файловый дескриптор может иметь несколько имен в файловой системе или вообще не иметь имени.

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

Тайлер МакГенри
источник
4
тогда применим мой ответ. В Linux для этого нет средств. Дескрипторы файлов Linux (POSIX) не обязательно относятся к файлам, и даже если они ссылаются на inodes, а не на имена файлов. Дескриптор может указывать на удаленный файл (который, следовательно, не имеет имени, это обычный способ создания временных файлов), или он может указывать на индексный дескриптор с несколькими именами (жесткие ссылки).
Tyler McHenry
3
Попробуйте взглянуть на исходный код lsof. :) Это то, что я сделал, когда сам недавно задавал этот же вопрос. lsof работает с черной магией и жертвенными козлами - вы не можете надеяться повторить его поведение. Чтобы быть более конкретным, lsof тесно связан с ядром Linux и не делает то, что делает, с помощью какого-либо API, доступного для пользовательского кода.
Тайлер МакГенри
28
В Linux для этого есть непереносимый proc API. Ограничения действительно существуют, но утверждение, что это невозможно, просто ложно.
bdonlan
1
@Tyler - lsof работает в пользовательском пространстве. Следовательно, есть API для всего, что он делает, доступный для кода пользователя :)
bdonlan
1
@Duck, переносимость, вероятно, объясняет, почему в исходном коде lsof так много черной магии; каждый вариант UNIX делает это по-своему. Интерфейсы linux proc не так уж и плохи, на самом деле, алебит довольно скудно документирован.
bdonlan