programing

C의 파일 설명자에서 파일 이름 검색

goodsources 2022. 9. 1. 23:23
반응형

C의 파일 설명자에서 파일 이름 검색

파일 디스크립터(Linux)의 파일명을 C로 얻을 수 있습니까?

에 사용할 수 있습니다./proc/self/fd/NNN여기서 NNN은 파일 기술자입니다.그러면 파일을 열었을 때와 같은 파일 이름이 표시됩니다.다만, 그 이후에 파일이 이동 또는 삭제되었을 경우, 더 이상 정확하지 않을 수 있습니다(Linux가 이름을 추적할 수 있는 경우도 있습니다).확인하기 위해stat지정된 파일 이름 및fstat가지고 계신 fd가st_dev그리고.st_ino똑같아요.

물론 모든 파일 기술자가 파일을 참조하는 것은 아닙니다.파일 기술자의 경우 다음과 같은 이상한 텍스트 문자열을 볼 수 있습니다.pipe:[1538488]실제 파일명은 모두 절대 경로이기 때문에 어느 것이 충분한지 쉽게 판단할 수 있습니다.또한 다른 사용자가 지적했듯이 파일에는 여러 개의 하드링크가 있을 수 있습니다.이러한 링크는 열린 하드링크만 보고됩니다.특정 파일의 모든 이름을 찾으려면 파일 시스템 전체를 이동하기만 하면 됩니다.

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_MAXsyslimits도 괜찮을 겁니다.

Windows 에서는, GetFileInformationByHandleEx 를 사용해 FileNameInfo 를 전달하면, 파일명을 취득할 수 있습니다.

fstat()를 사용하여 파일 inode를 structure stat별로 가져올 수 있습니다.그런 다음 readdir()를 사용하여 검색한 inode를 디렉토리 내에 존재하는 inode(Struct Drient)와 비교하여(디렉토리를 알고 있다고 가정하고), 대응하는 파일명을 찾을 수 있습니다.심술궂나요?

Tyler가 지적한 바와 같이, 특정 FD는 파일명이 0(다양한 경우) 또는 > 1(복수의 「하드 링크」는 일반적으로 후자의 상황을 나타내는 방법)에 대응하고 있기 때문에, 「직접적이고 신뢰성 있는」을 실행할 수 있는 방법은 없습니다.그래도 모든 제한이 있는 기능이 필요한 경우(1이 아닌 0, 2, ...의 결과를 얻을 수 있는 속도와 가능성)는 다음과 같습니다.먼저 FD를 fstat합니다.이것에 의해, 결과적으로는,struct stat파일 저장 장치, 하드 링크 수, 특수 파일인지 여부 등이것은 이미 질문에 대답한 것일 수 있습니다.예를 들어 0개의 하드링크가 있는 경우, 대응하는 파일명이 디스크에 존재하지 않는 것을 알 수 있습니다.

통계정보에 희망이 있는 경우 모든 하드링크를 찾을 때까지(또는 1개 이상 필요하지 않은 경우에는 첫 번째 링크만 찾을 수 있을 때까지) 관련 디바이스 상의 디렉토리의 "트리 산책"을 해야 합니다.이를 위해 readdir(및 opendir &c)를 사용하여 서브 디렉토리를 재귀적으로 엽니다.struct dirent따라서 원본과 동일한 inode 번호를 받았습니다.struct stat(이 때 이름뿐만 아니라 전체 경로를 원하는 경우 디렉토리 체인을 뒤로 돌려 재구성해야 합니다).

이 일반적인 접근법이 허용 가능하지만 좀 더 자세한 C 코드가 필요한 경우에는 작성하기가 어렵지 않습니다(예: 성능이 필연적으로 느려지거나 응용 프로그램의 목적으로 != 1 결과를 얻을 수 있는 가능성을 견딜 수 없습니다;-).

이것을 불가능하다고 치부하기 전에 먼저 lsof 명령어의 소스 코드를 확인해 보시기 바랍니다.

제한이 있을 수 있지만 파일 기술자와 파일 이름을 결정할 수 있을 것 같습니다.이 정보는 /proc 파일 시스템에 존재하므로 프로그램에서 가져올 수 있습니다.

무리다.파일 디스크립터는 파일시스템에 여러 개의 이름이 있거나 이름이 전혀 없는 경우가 있습니다.

편집: OS를 지정하지 않았기 때문에 OS 고유의 API가 없는 단순한 오래된 POSIX 시스템을 말하는 것으로 가정합니다.

이를 , 몇 합니다. 해야 .SD에서 이것을 하기 위한 공식 API는 없지만, 매우 복잡한 몇 가지 회피책이 있지만 다음 코드로도 가능합니다. 링크해야 합니다.-lkvm ★★★★★★★★★★★★★★★★★」-lc파일 시스템을 통과하기 위해 FTS를 사용하는 코드는 이 답변에서 나온 것입니다.

#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;
}

파일이 존재하지 않는 등의 이유로 파일을 찾을 수 없는 경우 빈 문자열이 반환됩니다.파일을 이동했을 경우, 파일을 휴지통으로 이동했을 때, FTS에 의해 그 위치가 검색되지 않은 경우, 파일의 새로운 위치가 대신 반환됩니다.파일이 많은 파일 시스템에서는 속도가 느려집니다.

파일을 찾지 않고 파일 시스템 전체의 디렉토리 트리에서 더 깊이 검색할수록 레이스 조건이 발생할 가능성이 높아집니다.다만, 퍼포먼스가 뛰어나기 때문에, 레이스 조건이 발생할 가능성은 매우 낮습니다.나는 나의 OpenB를 알고 있다.SD 솔루션은 C가 아닌 C++입니다.자유롭게 C로 변경하시면 대부분의 코드 로직이 동일합니다.시간이 된다면 빨리 C로 다시 쓰도록 하겠습니다.이 솔루션은 macOS와 마찬가지로 Windows 및 하나의 하드 링크만 사용할 수 있는 다른 플랫폼과의 휴대성을 위해 무작위로 하드 링크를 가져옵니다(인용 필요).크로스 플랫폼이 되는 것을 신경쓰지 않고 모든 하드링크를 취득하고 싶다면 while loop에서 break을 삭제하고 벡터를 반환할 수 있습니다.DragonFly BSD와 NetBSD는 현재 질문의 macOS 솔루션과 동일한 솔루션(정확한 코드)을 가지고 있습니다.수동으로 확인했습니다.macOS 사용자가 프로세스 ID를 삽입하여 열린 파일 기술자에서 경로를 가져오고 호출하는 프로세스에만 국한되지 않고 모든 하드 링크를 랜덤으로 제한하지 않고 얻기를 원하는 경우 다음 답변을 참조하십시오.Linux 및 기타 보다 간단하고 정확한 솔루션에서의 속도와 마찬가지로 파일 시스템 전체를 통과하는 것보다 훨씬 더 높은 성능을 발휘해야 합니다.FreeBSD 사용자는 이 질문에서 원하는 것을 얻을있습니다.이 질문에서 언급된 OS 수준의 버그는 이후 새로운 OS 버전에서 해결되었기 때문입니다.

호출 프로세스에 의해 열린 파일 기술자의 경로만 취득할 수 있는 보다 일반적인 솔루션이 있습니다.다만, 하드 링크나 레이스 조건에 관해서도, 이전의 솔루션과 같은 염려를 가지는 대부분의 Unix-likes에 대해서 즉시 사용할 수 있습니다.다만, if-the-the-f-loops등의 이유로, 퍼포먼스가 약간 빨라집니다.

#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;
}

호출 프로세스에 한정되어 있지만 퍼포먼스가 다소 향상되어야 하는 보다 빠른 솔루션에서는 기본적으로 std::unordered_map에 해당하는 C를 저장하는 도우미 함수를 사용하여 fopen() 및 open()에 대한 모든 콜을 랩하고 전달되는 절대 경로 버전과 파일 기술자를 쌍으로 구성할 수 있습니다.fopen()/open() 래퍼(및 _wopen_s()와 같은 UWP에서는 동작하지 않는 Windows 전용 등가물과 UTF-8을 지원하기 위한 모든 넌센스)를 Unix-likes에서는 realpath()로 실행할 수 있습니다.Windows에서는 GetFullPathNameW()로 실행할 수 있습니다.realpath()는 심볼릭링크(Windows에서 일반적으로 사용되지 않는 링크)를 해결하고 realpath() / GetFullPathNameW()는 열어둔 기존 파일을 상대 경로에서 절대 경로로 변환합니다.파일 기술자 및 절대 경로에는 std::unordered_map에 상당하는 C가 저장되어 있습니다(malloc()'d, 최종적으로는 free()'d int 및 c-string 어레이를 사용하여 직접 작성해야 합니다).이 역시 파일 시스템의 동적 검색을 실행하는 다른 솔루션보다 빠릅니다.단, 그 제한은 매력적이지 않습니다.ion, 즉 파일 시스템에서 이동된 파일은 기록되지 않지만 적어도 파일이 존재 여부를 테스트하기 위해 자신의 코드를 사용하여 삭제되었는지 여부를 확인할 수 있습니다.또한 파일을 열고 디스크립터에 대한 경로를 메모리에 저장한 이후 파일이 대체되었는지 여부에도 기록되지 않습니다.따라서 outda를 제공합니다.ted 결과가 나올 수 있습니다.코드 예를 보고 싶으시면 알려주세요.단, 파일 위치 변경으로 인해 이 솔루션은 권장하지 않습니다.

언급URL : https://stackoverflow.com/questions/1188757/retrieve-filename-from-file-descriptor-in-c

반응형