在C语言中,是否可以根据文件描述符(Linux)获取文件名?
在C语言中,是否可以根据文件描述符(Linux)获取文件名?
/proc/self/fd/NNN
上使用readlink
,其中NNN是文件描述符。这将给出文件名,当它被打开时,它的名字 - 但是,如果文件自那时以来已经被移动或删除,它可能不再准确(尽管Linux在某些情况下可以跟踪重命名)。要验证,请获取给定的文件名的stat
和您拥有的文件描述符的fstat
,并确保st_dev
和st_ino
相同。pipe:[1538488]
。由于所有真实的文件名都将是绝对路径,因此您可以轻松确定哪些是这些文件描述符。此外,正如其他人所指出的那样,文件可以具有指向它们的多个硬链接 - 这只会报告它打开的一个。如果您想查找给定文件的所有名称,您将需要遍历整个文件系统。setuid()
技巧,那么你的进程可能无法访问 /proc/self/fd
。请参见:http://permalink.gmane.org/gmane.linux.kernel/1302546 - David Given我在Mac OS X上遇到了这个问题。我们没有/proc
虚拟文件系统,因此已接受的解决方案无法使用。
相反,我们有一个用于fcntl
的F_GETPATH
命令:
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
定义在哪里,所以我认为使用syslimits中的PATH_MAX
就可以了。
== 1
并在 if
语句中处理失败,还有下面涉及到 filePath
的内容。 - Kalle Richterstruct stat
中,可以得知文件位于哪个设备上,有多少个硬链接以及是否为特殊文件等等。这也许已经回答了你的问题--例如,如果硬链接数为0,你就会知道磁盘上实际上没有相应的文件名。struct dirent
中找到与原始struct stat
中具有相同inode号的项(此时,如果你需要整个路径而不仅仅是名称,你需要向后遍历目录链来重建它)。在OpenBSD上没有官方API可以做到这一点,但是通过一些非常复杂的解决方法,仍然可以使用以下代码实现,注意需要链接-lkvm
和-lc
。使用FTS遍历文件系统的代码来自this answer。
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <sys/stat.h>
#include <fts.h>
#include <sys/sysctl.h>
#include <kvm.h>
using std::string;
using std::vector;
string pidfd2path(int pid, int fd) {
string path; char errbuf[_POSIX2_LINE_MAX];
static kvm_t *kd = nullptr; kinfo_file *kif = nullptr; int cntp = 0;
kd = kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, errbuf); if (!kd) return "";
if ((kif = kvm_getfiles(kd, KERN_FILE_BYPID, pid, sizeof(struct kinfo_file), &cntp))) {
for (int i = 0; i < cntp; i++) {
if (kif[i].fd_fd == fd) {
FTS *file_system = nullptr; FTSENT *child = nullptr; FTSENT *parent = nullptr;
vector<char *> root; char buffer[2]; strcpy(buffer, "/"); root.push_back(buffer);
file_system = fts_open(&root[0], FTS_COMFOLLOW | FTS_NOCHDIR, nullptr);
if (file_system) {
while ((parent = fts_read(file_system))) {
child = fts_children(file_system, 0);
while (child && child->fts_link) {
child = child->fts_link;
if (!S_ISSOCK(child->fts_statp->st_mode)) {
if (child->fts_statp->st_dev == kif[i].va_fsid) {
if (child->fts_statp->st_ino == kif[i].va_fileid) {
path = child->fts_path + string(child->fts_name);
goto finish;
}
}
}
}
}
finish:
fts_close(file_system);
}
}
}
}
kvm_close(kd);
return path;
}
int main(int argc, char **argv) {
if (argc == 3) {
printf("%s\n", pidfd2path((int)strtoul(argv[1], nullptr, 10),
(int)strtoul(argv[2], nullptr, 10)).c_str());
} else {
printf("usage: \"%s\" <pid> <fd>\n", argv[0]);
}
return 0;
}
在整个文件系统的目录树中进行深度搜索,如果没有找到文件,则可能会出现竞争条件,尽管由于其高性能,这种情况仍然很少见。我知道我的OpenBSD解决方案是C++而不是C。如果您愿意,可以将其更改为C,大部分代码逻辑将保持不变。如果有时间,我希望很快就能用C重写它。与macOS一样,此解决方案随机获得一个硬链接(需要引用),以便在Windows和其他只能获得一个硬链接的平台上具有可移植性。如果您不关心跨平台并且想获取所有硬链接,则可以删除while循环中的break并返回一个向量。{{link1:DragonFly BSD和NetBSD与当前问题的macOS解决方案相同(完全相同的代码)},我已经手动验证过了。如果macOS用户希望从任何进程打开的文件描述符中获取路径,并插入进程ID,而不仅仅限于调用者,同时还可以获取所有可能的硬链接,而不仅仅是随机的一个,请参见{{link2:此答案}}。它应该比遍历整个文件系统要快得多,类似于Linux和其他更直接和简洁的解决方案。{{link3:FreeBSD用户可以在此问题中找到他们要寻找的内容},因为该问题中提到的操作系统级错误已经针对新的操作系统版本得到了解决。
这里有一个更通用的解决方案,它只能检索由调用进程打开的文件描述符的路径,但它应该可以在大多数类Unix系统上直接使用,与前面的解决方案相同,对于硬链接和竞争条件存在相同的问题,尽管由于if-then、for循环等较少,因此执行速度略快:
#include <string>
#include <vector>
#include <cstring>
#include <sys/stat.h>
#include <fts.h>
using std::string;
using std::vector;
string fd2path(int fd) {
string path;
FTS *file_system = nullptr; FTSENT *child = nullptr; FTSENT *parent = nullptr;
vector<char *> root; char buffer[2]; strcpy(buffer, "/"); root.push_back(buffer);
file_system = fts_open(&root[0], FTS_COMFOLLOW | FTS_NOCHDIR, nullptr);
if (file_system) {
while ((parent = fts_read(file_system))) {
child = fts_children(file_system, 0);
while (child && child->fts_link) {
child = child->fts_link; struct stat info = { 0 };
if (!S_ISSOCK(child->fts_statp->st_mode)) {
if (!fstat(fd, &info) && !S_ISSOCK(info.st_mode)) {
if (child->fts_statp->st_dev == info.st_dev) {
if (child->fts_statp->st_ino == info.st_ino) {
path = child->fts_path + string(child->fts_name);
goto finish;
}
}
}
}
}
}
finish:
fts_close(file_system);
}
return path;
}
不可能的。一个文件描述符在文件系统中可能有多个名称,或者根本没有名称。
编辑:假设您谈论的是一个普通的 POSIX 系统,没有任何特定于操作系统的 API,因为您没有指定操作系统。