symlink -
シンボリックリンクの取り扱い
シンボリックリンクは他のファイルへのポインターとして振る舞うファイルである。
その挙動を理解するには、まずハードリンクがどのように機能するかを理解しておかなければならない。
あるファイルへのハードリンクは、
元々のファイルと区別することができない。
なぜなら、
ハードリンクは元々のファイル名の裏にあるオブジェクトへの参照だからである。
(より正確には、
あるファイルへのハードリンクはそれぞれ同じ
inode 番号
への参照である。 inode
番号は inode
テーブルへのインデックスで、
inode
テーブルはファイルシステム上のすべてのファイルについてのメタデータを保持している。
stat(2) 参照。)
ファイルへの変更は、ファイルの参照に使用された名前とは独立に行われる。
ハードリンクはディレクトリを参照することはできない
(これはファイルシステムツリー内でループが発生する可能性を防止するためであり、
ループが発生すると、
多くのプログラムが混乱してしまうことだろう)。
また、
ハードリンクは異なるファイルシステム上のファイルを参照することもできない
(inode
番号はファイルシステムをまたがると一意ではないからである)。
シンボリックリンクは特別な種類のファイルで、
ファイルの内容はそのリンクの参照先の別のファイルのパス名を示す文字列である
(シンボリックリンクの内容は
readlink(2)
を使って読むことができる)。
言い換えると、
シンボリックリンクは別の名前へのポインターであり、
ファイルの裏にあるオブジェクトへのポインターではない。
この理由から、
シンボリックリンクではディレクトリへの参照やファイルシステム境界を越える参照を行うことができる。
シンボリックリンクが参照する先のパス名が存在しないといけないという要件はない。
存在しないパス名を参照するシンボリックリンクは「壊れた
(dangling)
リンク」と呼ばれる。
シンボリックリンクとその参照先のオブジェクトは一つのファイルシステムの名前空間内に共存するので、
リンクそのものと参照先のオブジェクトの間で混乱が生じる可能性がある。
かなり昔からあるシステムでは、
コマンドやシステムコールはいくらかアドホックな方法の独自のリンクの辿り方の決まり事を採用している。
ここでは、 Linux
や他のシステムで実装されている、
もっと広く使われている方法のルールについて概要を説明する。
サイト固有のアプリケーションもこれらのルールに準拠し、
可能な限りユーザーインターフェースが一貫したものになるようにすることが重要である。
There is a special class of symbolic-link-like objects known as "magic
links", which can be found in certain pseudofilesystems such as
proc(5) (examples include
/proc/[pid]/exe and
/proc/[pid]/fd/*). Unlike normal symbolic links, magic links are not
resolved through pathname-expansion, but instead act as direct references to
the kernel's own representation of a file handle. As such, these magic links
allow users to access files which cannot be referenced with normal paths (such
as unlinked files still referenced by a running program ).
Because they can bypass ordinary
mount_namespaces(7)-based restrictions,
magic links have been used as attack vectors in various exploits.
既存のシンボリックリンクの所有者とグループは
lchown(2)
を使って変更することができる。
シンボリックリンクの所有権が問題となる場面は、
スティッキービット (
stat(2) 参照)
がセットされたディレクトリで、
そのリンクの削除や名前の変更を行おうとしている場合だけである。
シンボリックリンクの最終アクセス時刻と最終修正時刻は
utimensat(2) や
lutimes(3)
で変更できる。
Linux
では、通常のシンボリックリンクのアクセス許可
(permission)
はどの操作でも使用されない。
アクセス許可は常に 0777
(すべてのユーザーカテゴリーにおいて読み出し、書き込み、実行が可能)
で、変更できない。
However, magic links do not follow this rule. They can have a non-0777 mode,
though this mode is not currently used in any permission checks.
open(2) に
O_PATH と
O_NOFOLLOW
の両方のフラグを指定すると、ファイルディスクリプターが得られる。このファイルディスクリプターは
fstatat(2),
fchownat(2),
fchmodat(2),
linkat (2),
readlinkat(2)
などのシステムコールの
dirfd
引数として渡して、
(シンボリックリンクが参照するファイルではなく)
シンボリックリンク自身に対する操作を行うことができる。
デフォルトでは
(すなわち
AT_SYMLINK_FOLLOW
フラグが指定されなかった場合)、
name_to_handle_at(2)
がシンボリックリンクに適用された場合、
(シンボリックリンクが参照するファイルではなく)
シンボリックリンクへのハンドルが返される。
それ以降の
open_by_handle_at(2) で
O_PATH
フラグを指定することで、
(シンボリックリンクが参照するファイルではなく)
シンボリックリンクに対するファイルディスクリプターを得ることができる。
繰り返しになるが、
このファイルディスクリプターを上述のシステムコールで使用し、
シンボリックリンク自身に操作を行うことができる。
シンボリックリンクは、
リンク自身に対する操作か、
リンクが参照するオブジェクトに対する操作のいずれかとして扱われる。
後者の場合、
アプリケーションやシステムコールはリンクを
辿る
(follow)と呼ばれる。
シンボリックリンクは他のシンボリックリンクを参照することもできる。
この場合、
シンボリックリンクでないオブジェクトが見つかるか、
存在しないファイルを参照するシンボリックリンクが見つかるか、
ループが検出されるまで、
リンクの展開が行われる。
(ループの検出は辿ることができるリンクの数に上限を設けることで行われる。
この上限を超過した場合はエラーとなる。)
3
つの領域に分けて議論する必要がある。以下の
3 つである。
- 1.
- システムコールのファイル名引数としてシンボリックリンクが使用される場合。
- 2.
- ファイルツリーを辿っていないユーティリティーのコマンドライン引数としてシンボリックリンクが指定される場合。
- 3.
- ファイルツリーを辿っているユーティリティーがシンボリックリンクを見つけた場合
(コマンドラインで指定される場合もあれば、
ファイル階層を辿っている途中で遭遇する場合もある)。
Before describing the treatment of symbolic links by system calls and commands,
we require some terminology. Given a pathname of the form
a/b/c, the
part preceding the final slash (i.e.,
a/b) is called the
dirname
component, and the part following the final slash (i.e.,
c) is called
the
basename component.
最初の領域は、システムコールのファイル名引数としてシンボリックリンクが使用される場合である。
The treatment of symbolic links within a pathname passed to a system call is as
follows:
- 1.
- Within the dirname component of a pathname, symbolic links
are always followed in nearly every system call. (This is also true for
commands.) The one exception is openat2(2), which provides flags
that can be used to explicitly prevent following of symbolic links in the
dirname component.
- 2.
- Except as noted below, all system calls follow symbolic
links in the basename component of a pathname. For example, if there were
a symbolic link slink which pointed to a file named afile,
the system call open("slink" ...) would return a file
descriptor referring to the file afile.
Various system calls do not follow links in the basename component of a
pathname, and operate on the symbolic link itself. They are:
lchown(2),
lgetxattr(2),
llistxattr(2),
lremovexattr(2),
lsetxattr(2),
lstat(2),
readlink(2),
rename(2),
rmdir(2), and
unlink(2).
Certain other system calls optionally follow symbolic links in the basename
component of a pathname. They are:
faccessat(2),
fchownat(2),
fstatat(2),
linkat(2),
name_to_handle_at(2),
open(2),
openat(2),
open_by_handle_at(2), and
utimensat(2); see their manual pages for details. Because
remove(3) is an alias for
unlink(2), that library function also
does not follow symbolic links. When
rmdir(2) is applied to a symbolic
link, it fails with the error
ENOTDIR.
link(2)
については特別に議論が必要である。
POSIX.1-2001 では
link(2) は
oldpath
がシンボリックリンクであればこれを展開するように規定している。
しかしながら、 Linux
はシンボリックリンクを展開しない。
(デフォルトでは Solaris
も同じだが、
適切なコンパイラーオプションを指定することで
POSIX.1-2001
で規定された動作をさせることができる。)
POSIX.1-2008
では、どちらの動作の実装も認められるように規定が変更された。
二つ目の領域は、
ファイルツリーを辿らないコマンドの、
コマンドライン引数のファイル名としてシンボリックリンクが指定される場合である。
以下に述べる場合を除くと、
コマンドはコマンドライン引数で指定された名前のシンボリックリンクを辿る。
例えば、
afile
という名前のファイルを指しているシンボリックリンク
slink
があったとすると、
コマンド
cat slink は
afile
の内容を表示することになる。
大事な点として意識しておくべきなのは、
このルールが適用されるコマンドの中には、
オプション次第ではファイルツリーを辿る場合があるコマンドもあるということである。
例えば、 コマンド
chown
file
はこのルールに含まれるが、
コマンド
chown -R file
はツリーを辿る動作をするのであてはまらない
(後者の場合は、3
つ目の領域に該当する)。
シンボリックリンクを辿るのではなく、
コマンドがシンボリックリンク自身に対して操作を行うことを明示的に指示したい場合、
例えば、
chown slink で
slink
がシンボリックリンクかどうかに関わらず、
slink
のファイル自身の所有権を変更したい場合は、
-h
オプションを使用すべきである。
上記の例では、
chown root
slink は
slink
が参照するファイルの所有権を変更するが、
chown -h root slink は
slink
自身の所有権を変更する。
このルールにはいくつかの例外がある。
- *
- コマンド mv(1) と
rm(1)
は引数で指定された名前のシンボリックリンクを辿らないが、
それぞれシンボリックリンク自身の名前変更と削除を行おうとする。
(シンボリックリンクが相対パスでファイルを参照している場合、
そのシンボリックリンクを別のディレクトリに移動すると、動かなくなることが非常によくある。
移動の結果、
パスが正しくないものになってしまうからである。)
- *
-
ls(1)
コマンドもこのルールの例外である。
昔からあるシステムとの互換性のため
( ls(1)
がツリーを辿らない場合、つまり
-R
オプションが指定されなかった場合)、
ls(1)
コマンドはオプション
-H か -L
が指定された場合、もしくはオプション
-F, -d, -l
が指定されなかった場合、
引数として指定されたシンボリックリンクを辿る。
( ls(1) コマンドは、
ファイルツリーを辿らない場合であっても、
オプション -H と -L
がその動作に影響を与える唯一のコマンドである。)
- *
-
file(1)
コマンドもこのルールの例外である。
file(1) コマンドは、
デフォルトでは引数で指定されたシンボリックリンクを辿らない。
file(1) コマンドは、 -L
オプションが指定された場合、
引数で指定されたシンボリックリンクを辿る。
次のコマンドは指定された場合もしくは常にファイルツリーを辿る:
chgrp(1),
chmod(1),
chown(1),
cp(1),
du(1),
find(1),
ls(1),
pax(1),
rm(1),
tar(1)。
重要なのは、
ファイルツリーを辿っている際に見つかったシンボリックリンクにも、
コマンドライン引数として渡されたシンボリックリンクにも、
以下のルールが等しく適用される点である。
「1
つ目のルール」は、
ディレクトリ以外のファイルを参照するシンボリックリンクに適用される。
シンボリックリンクに適用される操作はシンボリックリンク自身に行われるが、
そうでない場合はリンクは無視される。
コマンド
rm -r slink directory は
slink
を削除するとともに、
ファイルツリーを辿る途中で見つけたシンボリックリンクも削除する。
シンボリックリンクは削除できるからである。
rm(1) が
slink
が参照するファイルに影響をおよぼすことはない。
「2
つ目のルール」は、
ディレクトリを参照するシンボリックリンクに適用される。
デフォルトでは、
ディレクトリを参照するシンボリックリンクを辿らない。
この動作はしばしば「物理的な」ツリー探索
("physical" walk) と呼ばれる。
これに対して
(ディレクトリを参照するシンボリックリンクを辿る場合は)
「論理的な」ツリー探索
("logical" walk) と呼ばれる。
一貫性を持たせるため、ファイルツリーを辿るコマンドが可能な限り従っている慣習がいくつかある。
- *
-
-H ("half-logical")
フラグを指定すると、
参照先のファイル種別に関わらず、
コマンドにコマンドラインで指定されたシンボリックリンクを辿らせることができる。
このフラグは、
コマンドラインの名前空間を論理的な名前空間のように見せるためのものである。
(常にファイルツリーを辿るわけではないコマンドでは、
-R
フラグを一緒に指定しない限り、
-H
フラグは無視される点に注意。)
- 例えば、
コマンド chown -HR user slink
は slink
が指すファイルを頂点とするファイル階層を辿る。
-H は上記で説明した
-h
フラグとは同じではないことに注意。
-H
フラグを指定すると、
アクションを実行する場合でも、
ツリーを辿る場合でも、
コマンドラインで指定されたシンボリックリンクの解決
(dereference) を行う。
ユーザーがシンボリックリンクが指すファイル名を指定したのと同じように見える。
- *
-
-L ("logical")
フラグを指定すると、
参照先のファイル種別に関わらず、
コマンドが、
コマンドラインで指定された名前のシンボリックリンクも、
ファイルツリーを辿る際に見つけたシンボリックリンクも辿るようになる。
このフラグは、
名前空間全体を論理的な名前空間のように見せるためのものである。
(常にファイルツリーを辿るわけではないコマンドでは、
-R
フラグを一緒に指定しない限り、
-L
フラグは無視される点に注意。)
- 例えば、
コマンド chown -LR user slink
は slink
が参照するファイルの所有者を変更する。
slink
がディレクトリを参照する場合、
chown
はそのシンボリックリンクが参照するディレクトリを頂点とするファイル階層を辿る。
また、 chown
が辿るファイルツリー内でシンボリックリンクが見つかった場合、
slink
と同じように処理される。
- *
-
-P ("physical")
フラグを指定すると、
コマンドはデフォルトの動作をするようになる。
このフラグは名前空間全体を物理的な名前空間のように見せるためのものである。
デフォルトでファイルツリーを辿らないコマンドでは、
-R
フラグが同時に指定されなかった場合、
フラグ
-H,
-L,
-P
は無視される。
また、
-H,
-L,
-P
は複数回同時に指定できるが、
最後に指定されたオプションでコマンドの動作が決定される。
この動作は、
コマンドのエイリアスにある動作を指定しておいて、
コマンドラインでその動作を上書きできるようにするためである。
コマンド
ls(1) と
rm(1)
には、
これらのルールに対する例外がある。
- *
-
rm(1)
コマンドは、
参照先のファイルではなく、シンボリックリンクに対して操作を行う。
したがって、
シンボリックリンクを辿ることはない。
rm(1)
コマンドはオプション
-H, -L, -P
をサポートしていない。
- *
- 古いシステムとの互換性を持たせるため、
ls(1)
コマンドは少し違った動作をする。
オプション -F, -d, -l
を指定した場合、
ls(1)
はコマンドラインで指定されたシンボリックリンクを辿る。
-L
フラグが指定された場合、
コマンドラインで指定された場合でも、
ファイルツリーを辿る際に見つかった場合でも、
ファイル種別に関わらず、
ls(1)
はすべてのシンボリックリンクを辿る。
chgrp(1),
chmod(1),
find(1),
ln(1),
ls(1),
mv(1),
namei(1),
rm(1),
lchown(2),
link(2),
lstat(2),
readlink(2),
rename(2),
symlink(2),
unlink(2),
utimensat(2),
lutimes(3),
path_resolution(7)
この man ページは Linux
man-pages
プロジェクトのリリース
5.10
の一部である。プロジェクトの説明とバグ報告に関する情報は
https://www.kernel.org/doc/man-pages/
に書かれている。