저는 Linux 4.x 기반 배포판을 사용하고 있으며 최근에 커널의 open()
시스템 호출이 O_PATH
공개 플래그를 지원하는 것을 발견했습니다.
해당 페이지에는 이론적으로 사용할 수 있는 시스템 호출 목록이 있지만 man
그 아이디어가 무엇인지 잘 모르겠습니다. open(O_PATH)
파일이 아닌 디렉토리만 사용 합니까 ? 이렇게 하면 디렉터리 경로 대신 파일 설명자를 사용하는 이유는 무엇입니까? 또한 거기에 나열된 대부분의 시스템 호출은 디렉토리별로 다르지 않은 것 같습니다. 그러면 O_PATH
해당 디렉토리를 파일 설명자로 사용하여 일반 파일도 열 수 있습니까? 아니면 파일 설명자를 구했지만 기능이 제한되어 있나요?
O_PATH
누군가 우리가 그것을 어떻게 사용해야 하고 어떻게 사용해야 하는지 에 대해 설득력 있는 설명을 줄 수 있습니까 ?
노트:
- 필요하지 않는 한 그것이 어떻게 발전했는지에 대한 이력을 설명할 필요가 없습니다(관련 매뉴얼 페이지에 Linux 2.6.x, 3.5 및 3.6의 변경 사항이 언급되어 있습니다). 단지 현재 위치가 궁금할 뿐입니다.
- libc나 다른 상위 수준 기능만 사용하라고 말하지 마세요. 저도 알고 있습니다.
답변1
설명open(2)
매뉴얼 페이지는 시작하기 위한 몇 가지 단서를 제공합니다.
O_PATH (since Linux 2.6.39)
Obtain a file descriptor that can be used for two purposes:
to indicate a location in the filesystem tree and to per‐
form operations that act purely at the file descriptor
level. The file itself is not opened, and other file oper‐
ations (e.g., read(2), write(2), fchmod(2), fchown(2),
fgetxattr(2), ioctl(2), mmap(2)) fail with the error EBADF.
때로는 파일이나 디렉터리를 열고 싶지 않을 때도 있습니다. 대신 특정 작업을 수행하려면 이 파일 시스템 개체에 대한 참조만 필요합니다(예: fchdir()
열린 파일 설명자로 참조하는 디렉터리 O_PATH
). 따라서 주목하는 것이 중요합니다. 이것이 우리의 목적이라면 O_PATH
파일 자체가 실제로 열리지 않기 때문에 여는 것이 더 저렴해야 합니다.
그리고 덜 중요한 점: 존재하기 전에는 O_PATH
파일 시스템 객체에 대한 참조를 얻는 방법은 를 사용하여 객체를 여는 것이었습니다 O_RDONLY
. 그러나 이를 사용하려면 O_RDONLY
객체에 대한 읽기 권한이 필요합니다. 그러나 실제로 개체를 읽을 필요가 없는 사용 사례도 많습니다. 예를 들어 바이너리를 실행하거나 디렉터리( fchdir()
)에 액세스하거나 디렉터리를 통해 디렉터리 내의 개체에 액세스하는 등입니다.
"*at()" 시스템 호출과 함께 사용됩니다.
의 일반적인(유일한 것은 아님) 용도는 , 등과 같은 O_PATH
"*at" 시스템 호출과 함께 사용하기 위해 참조할 수 있도록 디렉토리를 여는 것입니다. 비슷한 이름( ,, 으로 대략적으로 생각할 수 있는 이 시스템 호출 계열은 여러 가지 목적으로 사용됩니다. 디렉토리 경로 대신 파일 설명자를 사용하려면?" ". 매뉴얼 페이지를 더 자세히 살펴보면 다음 텍스트를 찾을 수 있습니다("*at" 시스템 호출 근거가 있는 부제목 아래). openat()
fstatat()
fchownat()
open()
fstat()
fchown()
open(2)
First, openat() allows an application to avoid race conditions
that could occur when using open() to open files in directories
other than the current working directory. These race conditions
result from the fact that some component of the directory prefix
given to open() could be changed in parallel with the call to
open(). Suppose, for example, that we wish to create the file
path/to/xxx.dep if the file path/to/xxx exists. The problem is
that between the existence check and the file creation step, path
or to (which might be symbolic links) could be modified to point
to a different location. Such races can be avoided by opening a
file descriptor for the target directory, and then specifying that
file descriptor as the dirfd argument of (say) fstatat(2) and ope‐
nat().
이를 좀 더 구체적으로 설명하자면... 현재 작업 디렉터리 외부의 디렉터리에서 여러 작업을 수행하려는 프로그램이 있다고 가정해 보겠습니다. 즉, 사용하는 파일 이름의 일부로 일부 디렉터리 접두어를 지정해야 한다는 의미입니다. 예를 들어, 경로 이름이 이라고 가정하면 /dir1/dir2/file
다음 두 가지 작업을 수행하려고 합니다.
- 몇 가지 확인을 수행합니다
/dir1/dir2/file
(예: 파일 소유자 또는 마지막 수정 날짜 등). - 이 검사 결과에 만족한다면 동일한 디렉터리에서 다른 파일 시스템 작업을 수행할 수도 있습니다(예:
/dir1/dir2/file.new
.
이제 먼저 전통적인 경로 이름 기반 시스템 호출을 사용하여 모든 작업을 수행했다고 가정해 보겠습니다.
struct stat stabuf;
stat("/dir1/dir2/file", &statbuf);
if ( /* Info returned in statbuf is to our liking */ ) {
fd = open("/dir1/dir2/file.new", O_CREAT | O_RDWR, 0600);
/* And then populate file referred to by fd */
}
이제 디렉터리 접두사에서 /dir1/dir2
구성 요소 중 하나(예 dir2
: )가 실제로 기호 링크(디렉토리 참조)이고통화 stat()
사이open()
dir2
악의적인 행위자는 다른 디렉터리를 가리키도록 심볼릭 링크의 대상을 변경할 수 있습니다 . 이것은 시간 경쟁 조건을 사용하는 고전적인 시간 확인입니다. 우리 프로그램은 한 디렉터리에서 파일을 검사했지만 속여서 다른 디렉터리(아마도 보안에 민감한 디렉터리)에 파일을 생성했습니다. 여기서 중요한 점은 경로 이름은 /dir/dir2
동일해 보이지만 참조하는 내용이 완전히 달라진다는 것입니다.
이러한 문제를 피하기 위해 "*at" 호출을 사용할 수 있습니다. 먼저 작업을 수행할 디렉터리에 대한 핸들을 얻습니다.
dirfd = open("/dir/dir2", O_PATH);
여기서 핵심 포인트 dirfd
는안정적인/dir1/dir2
를 호출할 때 경로가 참조하는 디렉터리에 대한 참조입니다 open()
. 이후에 기호 링크의 대상이 변경되더라도 이것이 참조하는 내용에는 dir2
영향을 미치지 않습니다 . 이제 dirfd
위의 stat()
and 호출에 상응하는 "*at" 호출을 사용하여 검사 + 작업을 수행 할 수 있습니다 open()
.
fstatat(dirfd, ""file", &statbuf)
struct stat stabuf;
fstatat(dirfd, "file", &statbuf);
if ( /* Info returned in statbuf is to our liking */ ) {
fd = openat(dirfd, "file.new", O_CREAT | O_RDWR, 0600);
/* And then populate file referred to by fd */
}
이 단계에서는 경로 이름의 심볼릭 링크를 조작해도 /dir/dir2
아무런 효과가 없습니다. 확인( fstatat()
) 및 조작( openat()
)은 동일한 디렉토리에서 발생하는 것이 보장됩니다.
"*at()" 호출을 사용하는 또 다른 목적이 있는데, 이는 멀티 스레드 프로그램에서 "스레드별 현재 작업 디렉터리"라는 아이디어와 관련이 있지만(열린 디렉터리를 다시 사용할 수 있음 O_PATH
) 이 용도가 아닐까 생각됩니다. 귀하의 질문과 관련이 있을 수 있습니다. 질문은 별로 관련이 없습니다. open(2)
더 알고 싶으면 매뉴얼 페이지를 읽어보도록 하겠습니다.
일반 파일의 파일 설명자와 함께 사용됩니다.
일반 파일의 한 가지 용도 O_PATH
는 실행 권한이 있는 바이너리 파일을 여는 것입니다(그러나 반드시 읽기 권한은 아니므로 파일을 여는 데 사용할 수 없습니다 O_RDONLY
). 그런 다음 이 파일 설명자를 다음으로 전달할 수 있습니다.fexecve(3)
프로그램을 실행합니다. 본질적으로 fexecve(fd, argv, envp)
모든 주장은 다음과 같습니다 .fd
snprintf(buf, "/proc/self/fd/%d", fd);
execve(buf, argv, envp);
(glibc 2.27부터 시작하더라도 구현에서는 대신 다음을 사용합니다.execveat(2)
시스템 호출을 제공하는 커널의 시스템 호출입니다. )