「O_PATH」は何のために、どのように使用すべきですか?

「O_PATH」は何のために、どのように使用すべきですか?

私は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/file2つの操作を実行したいとします。

  1. いくつかの確認を行います/dir1/dir2/file(ファイルの所有者や最後の変更日など)。
  2. このチェック結果に満足したら、同じディレクトリで別のファイルシステム操作を実行することもできます/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コンポーネントの1つ(例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)もっと知りたい場合は、マニュアルページを読んでみましょう。

通常ファイルのファイル記述子とともに使用されます。

通常のファイルの1つの目的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)システムコールを提供するカーネルのシステムコールです。 )

関連情報