「O_PATH」を何に使用するのですか?


8

Linux 4.xベースのディストリビューションを使用していますが、最近、カーネルのopen()システムコールがO_PATHオープンフラグをサポートしていることに気付きました。

そのmanページには理論的に使用できるシステムコールのリストがありますが、その考え方がよくわかりません。Iやるopen(O_PATH)だけのディレクトリ、ファイルではなく?そして、もしそうなら、なぜディレクトリのパスの代わりにファイル記述子を使用したいのですか?また、リストされているシステムコールのほとんどは、ディレクトリに固有のものではないようです。では、通常のファイルを開いてO_PATH、どういうわけかそれらのディレクトリをファイル記述子として取得しますか?または、それらのファイル記述子を取得するには機能が制限されていますか?

誰かが何O_PATHをどのように、そして何のために、私たちがそれを使うことになっているのかについて、説得力のある説明を与えることができますか?

ノート:

  • 必要な場合を除いて、これがどのように進化したかについての歴史を説明する必要はありません(関連するmanページではLinux 2.6.x、3.5、3.6での変更点について言及しています)。
  • libcやその他の高レベルの機能を使用するように言わないでください。


@sebasth:確かに関連していますが、次のようになります。1。これまでに少し古く、状況が変わっている可能性があります。2.率直に言って、私は答えの要点を完全には理解していません。
アインポクルム2017

1
その質問にコメントを投稿して、何か変更したかどうかを尋ねることができます。
Barmar 2017

回答:


8

open(2)manページの説明は、最初に手掛かりを与えます:

   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、ファイル自体は実際には開かれないので、with with を少し安くする必要があります。

そして、それほど重要ではない点:が存在する前O_PATHに、ファイルシステムオブジェクトへのそのような参照を取得する方法は、でオブジェクトを開くことでしたO_RDONLY。ただし、を使用するにO_RDONLYは、オブジェクトに対する読み取り権限が必要です。ただし、実際にオブジェクトを読み取る必要がないさまざまなユースケースがあります。たとえば、バイナリを実行したり、ディレクトリにアクセスしfchdir()たり()、ディレクトリを介してディレクトリ内のオブジェクトにアクセスしたりします。

「* at()」システムコールでの使用

一般的には、だけでなく、の使用はO_PATHのようなシステムコール、「時*」で使用するために、そのディレクトリを参照するようにするために、ディレクトリを開くことでopenat()fstatat()fchownat()、などを。私たちは大体似た名前を持つ古いシステムコールに近代的な後継者として考えることができ、システムコール、この家族(open()fstat()fchown()など)、目的のカップル、あなたは「頼むときあなたが触れるうち最初のサーブなぜディレクトリのパスの代わりにファイル記述子を使用したいのですか?」open(2)マニュアルページをさらに見ると、次のテキストが見つかります( "* at"システムコールの根拠のある小見出しの下)。

   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 open()呼び出しと同等の「* at」呼び出しを使用して、check +操作を実行できます。

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)。詳しく知りたい場合は、manページをお読みください。

通常のファイルのファイル記述子での使用

O_PATH通常のファイルでの1つの使用法は、実行権限を持つバイナリを開くことです(ただし、必ずしも読み取り権限ではないため、でファイルを開くことができません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)システムコールを提供するカーネルで、実装は代わりにシステムコールを使用します。)


The problem is that between the existence check and the file creation step, path or to ... could be modified -この文を解析できません。でも要点はわかったと思います。したがって、これはディレクトリの一種のロックメカニズムとして機能します。しかしopen()、実際のロックではなく結果を使用するのはなぜですか?
アインポクルム2017

@einpoklum問題は、 'path'と 'to'が元のmanページに示されたフォーマットを持たないことです。これらは、架空のパス名 "/ path / to / xxx"のコンポーネントです。そして、それはロックのようなものではありません。これは、ファイルシステムオブジェクトへの安定した参照です。複数のプログラムが同じオブジェクトへのそのような参照を持っている可能性があります。
mtk
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.