path_resolution - システムコールの説明 - Linux コマンド集 一覧表
名前
Unix/Linux path resolution - ファイル名で参照されるファイルを見つける
説明
いくつかの Unix/Linux システムコールは、
1 つ以上のファイル名を引き数として持つ。
ファイル名 (またはパス名) は以下のようにして解決される。
ステップ 1: 解決過程を開始する
パス名が '/' 文字で始まっている場合、
ディレクトリ検索の開始点は現在のプロセスのルートディレクトリになる。
(プロセスはルートディレクトリを親プロセスから継承する。
通常は、これがファイル階層のルートディレクトリになる。
プロセスは
chroot
(2) システムコールを使って別のルートディレクトリを取得することもできる。
この場合、そのプロセスと
CLONE_NEWNS フラグを設定して
clone
(2) を呼び出すことによって開始されたそのプロセスの子孫は、
完全にプライベートな名前空間を取得できる。)
パス名の '/' の部分は、このようにして扱われる。
パス名が '/' 文字で始まっていない場合、
解決過程におけるディレクトリ検索の開始点は、
プロセスの現在の作業 (working) ディレクトリとなる。
(これも親プロセスから継承される。
これは
chdir
(2) システムコールを使うことで変更できる。)
'/' 文字で始まるパス名は絶対パス名と呼ばれ、
'/' 文字で始まらないパス名は相対パス名と呼ばれる。
ステップ 2: パスを辿る
現在の検索ディレクトリをディレクトリ検索の開始点とする。
そして、パス名の最後の構成要素 (component) でない各構成要素について、
現在の検索ディレクトリで検索を行う。
ここで構成要素は '/' で区切られた部分文字列である。
プロセスが現在の検索ディレクトリの検索許可を持たない場合、
EACCES エラーが返される ("Permission denied")。
構成要素が見つからない場合、ENOENT エラーが返される
("No such file or directory")。
構成要素は見つかったが、ディレクトリでもシンボリックリンクでもない場合、
ENOTDIR エラーが返される ("Not a directory")。
構成要素が見つかって、かつディレクトリである場合、
現在の検索ディレクトリをそのディレクトリに設定し、
次の構成要素に移動する。
構成要素が見つかって、かつシンボリックリンク (symlink) である場合、
(現在の検索ディレクトリをディレクトリ検索の開始点として)
最初にそのシンボリックリンクを解決する。
結果がディレクトリでない場合、ENOTDIR エラーが返される。
シンボリックリンクの解決が成功してディレクトリが返された場合、
そのディレクトリを現在の検索ディレクトリとして設定し、
次の構成要素に移動する。
解決過程に再帰が含まれる点に注意すること。
カーネルをスタックオーバーフローや
サービス拒否 (denial of service) から守るため、
再帰の最大の深さとシンボリックリンクを辿る最大回数に制限がある。
最大値を超えた場合 ELOOP エラーが返される
("Too many levels of symbolic links")。
ステップ 3: 最後のエントリを見つける
パス名の最後の構成要素の検索は、前のステップで説明した
他の全ての構成要素と同じように実行されるが、2 つの違いがある。
(i) 最後の構成要素はディレクトリである必要がない
(パス解決過程に関する限りはどちらでも構わない \(em
特定のシステムコールが要求するものによって、
ディレクトリでなければならない場合もあるし、
ディレクトリ以外でなければならない場合もある)。
(ii) 構成要素が見つからない場合にエラーにする必要はない \(em
その構成要素を作成するだけでよい場合もある。
最後のエントリの詳細な扱いは、
特定のシステムコールの man ページで説明されている。
. と ..
慣習として、全てのディレクトリはエントリ "." と ".." を持つ。
これらはそれぞれ、そのディレクトリ自身とその親ディレクトリを参照する。
パス解決過程では、これらのエントリが物理的なファイルシステムに
実際に存在するか否かに関わらず、慣習的な意味を持つと仮定する。
ルートより上に辿ることはできない: "/.." は "/" と同じである。
マウント位置
"mount dev path" コマンドを実行した後、
パス名 "path" はデバイス "dev" 上のファイルシステム階層の
ルートディレクトリを参照するようになり、以前の位置を参照しない。
マウントされたファイルシステムの外に出ることができる:
"path/.." は "dev" 上のファイルシステム階層の外である
"path" の親ディレクトリを参照する。
末尾のスラッシュ
パス名が '/' で終わっている場合、
ステップ 2 において、その前にある構成要素の解決法を次のように強制する:
その構成要素が存在しなければならず、ディレクトリとして解決される。
存在しない場合は、末尾の '/' が無視される。
(また同様に、末尾に '/' があるパス名は、
'.' を末尾に加えて得られるパス名と等しい。)
最後がシンボリックリンクのとき
パス名の最後の構成要素がシンボリックリンクである場合、
参照されるファイルをシンボリックリンクとするか、
その内容についてパスを解決した結果とするかは、
システムコールに依存する。
たとえば、システムコール
lstat
(2)はシンボリックリンクに作用する。
一方、
stat
(2) はシンボリックリンクで指されたファイルに作用する。
長さの制限
パス名には最大長がある。
パス名 (またはシンボリックリンクを解決するときに得られる中間パス名) が
長すぎる場合、ENAMETOOLONG エラーが返される ("File name too long")。
空のパス名
元々の Unix では、空のパス名は現在のディレクトリを参照していた。
最近、POSIX では空のパス名を解決するべきではないという決定がなされた。
この場合、Linux は ENOENT を返す。
許可
ファイルの許可ビットは、3 組の 3 ビットから構成される。
chmod
(1) と
stat
(2) を参照すること。
現在のプロセスの実効ユーザ ID がファイルの所有者 ID と等しい場合、
3 つのうち最初のグループが使われる。
ファイルのグループ ID が現在のプロセスの実効グループ ID または
(
setgroups
(2)で設定される) 現在のプロセスの補助 (supplementary) グループ ID と等しい場合、
3 つのうち 2 番目のグループが使われる。
どちらにも当てはまらない場合、3 番目のグループが使われる。
3 ビットが使われる場合、最初のビットは読み込み許可を決定し、
2 番目のビットは書き込み許可を決定する。
また 3 番目のビットは、通常のファイルの場合は実行許可を表し、
ディレクトリの場合は検索許可を表す。
Linux は、許可のチェックにおいて、実効ユーザ ID ではなく fsuid を使う。
通常は fsuid は実効ユーザ ID と等しいが、fsuid はシステムコール
setfsuid
(2) で変更することができる。
(ここで "fsuid" は "file system user ID" を表している。
この概念は「プロセスが同じ実効ユーザ ID を持つプロセスに
同時にシグナルを送ることができる」というユーザ空間 NFS サーバを
実装する際に必要であった。
これは今では廃れてしまった。
setfsuid
(2) を使うべきではない。
同様に、Linux では実効グループ ID の代わりに
fsgid ("ファイルシステム・グループID") を使う。
setfsgid
(2) を参照すること。
許可の確認をスキップする: スーパーユーザとケーパビリティ
伝統的な Unix システムでは、スーパーユーザ
(
root
,ユーザ ID 0) は非常に強力であり、ファイルアクセス時の
許可による制限を全てスキップする。
Linux では、スーパーユーザ権限が複数のケーパビリティに分割されている
(
capabilities
(7)参照)。ファイルの許可の確認には、
CAP_DAC_OVERRIDE と CAP_DAC_READ_SEARCH の 2つのケーパビリティが関係する
(プロセスの fsuid が 0 の場合、そのプロセスはこれらのケーパビリティを持つ)。
CAP_DAC_OVERRIDE ケーパビリティは全ての許可チェックを上書きする。
実際には、対象となるファイルの 3 つの実行許可ビットのうちの
少なくとも 1 つが設定されていれば、実行を許可するだけである。
CAP_DAC_READ_SEARCH ケーパビリティは、
ディレクトリに対して読み込みと検索を許可し、
通常のファイルに対して読み込みを許可する。