次の例は、読み取り権限のみを持つファイルを作成する方法を示しています。ご覧のように、取得したechoコマンドを使用してこのファイルに書き込もうとすると、Permission denied
。
しかし、なぜ、viを使用する場合にnot get Permission denied
?ここに示すように、ファイルが読み取り専用であっても、ファイルに書き込むことができます。
ここで何が起きてるの?これはviのバグですか?
[admin@madona-machine1 ~]$ touch test-file
[admin@madona-machine1 ~]$ ls -ltr
total 0
-rw-r--r-- 1 admin admin 0 Apr 13 07:32 test-file
[admin@madona-machine1 ~]$ chmod -w test-file
[admin@madona-machine1 ~]$ ls -ltr
total 0
-r--r--r-- 1 admin admin 0 Apr 13 07:32 test-file
[admin@madona-machine1 ~]$ echo try_to_write > test-file
-bash: test-file: Permission denied
[admin@madona-machine1 ~]$ vi test-file
I am good singer,
~
~
~
~
~
~
~
"test-file" 1L, 4C written
注:レガシーライセンスの理由により、ほとんどのGNU/Linuxディストリビューションには、Bill Joyによって書かれたオリジナルのviプログラムが含まれていません。代わりに、viコマンドはvi互換モードでVimを実行することによって提供されます。次の回答は、vi互換モードでVimを実行することに基づいています。
Vimは、読み取り専用ファイル_W10: Warning: Changing a readonly file
_のバッファーを変更するとユーザーに警告します。ユーザーがこのファイルに書き込もうとすると、次のエラーメッセージ'readonly' option is set (add ! to override)
が表示されます。
Vimは役立つので、感嘆符_!
_をw
コマンドに追加することで、ユーザーが強制的に書き込みを要求できることをユーザーに知らせます。この強力なバージョンの書き込みコマンドを使用すると、Vimは元のファイルを削除します(VimをVimのみのbackup
オプションセットで使用すると、元のファイルは実際に名前が変更されますバックアップファイルと同じです)。次に、同じ名前で新しいファイルを開く(作成する)オリジナルとそのバッファの内容をこの新しいファイルに書き込みます。これは、Vimを実行する前後にファイルの inode を確認することで確認できます。
_$ ls -l --inode t
131529 -r--r--r-- 1 anthony anthony 0 Apr 13 09:23 t
$ vi t
$ ls -l --inode t
131649 -r--r--r-- 1 anthony anthony 4 Apr 13 09:23 t
_
注:これにより、ファイルの権限と所有権が変更され、(シンボリック)リンクが壊れる可能性があります。たとえば、元のファイルが別のユーザーによって所有されていた場合、新しいファイルはVimを実行しているユーザーによって所有されます。
プロセスがこれを実行できるのは、ファイルの親ディレクトリに対する書き込み権限がある場合のみです。一般に、プログラムがファイルを変更できないようにするには、ファイル自体とその親ディレクトリの両方の権限を保護する必要があります。
ただし、この場合でも、Vimはしつこいユーザーがファイルを上書きできるように最善を尽くします。 Vimユーザーがファイルの所有権を持っている場合、Vimは一時的にファイルの権限を変更し(chmod
システムコールを使用)、バッファをファイルに書き込み、閉じることにより、読み取り専用の親ディレクトリの制限を回避できます。ファイルを保存してから、権限を元に戻します。以下は、strace、_strace -o ../vi.trace vi t
_を介してviを実行中に行われたシステムコールの抜粋です。
_getuid() = 501
chmod("t", 0100644) = 0
open("t", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 4
write(4, "I am good singer,\n", 18) = 18
fsync(4) = 0
close(4) = 0
chmod("t", 0100444) = 0
_
注:Vimはファイルの権限を変更できないため、Vimユーザーが所有権のないファイルを編集している場合、これは起こりません。
(GNU/Linuxシステムで)ファイルを変更できないことを本当に確認するには、スーパーユーザーとしてchattr
コマンドを実行します。
_Sudo chattr +i filename
_
_man chattr
_から:
「i」属性のファイルは変更できません。削除や名前の変更はできません。このファイルへのリンクを作成したり、データをファイルに書き込んだりすることはできません。この属性を設定またはクリアできるのは、スーパーユーザーまたはCAP_LINUX_IMMUTABLE機能を持つプロセスだけです。
vi
、:w
、:wq
、または:x
のような通常の保存コマンドを使用する場合、たとえばZZ
を使用すると、すべてのvim
実装がファイルの書き込みを妨げます。
:w
E45: 'readonly' option is set (add ! to override)
一方、:x!
や:wq!
のようなものを使用して、vi
にその権限に関係なくファイルを書き込むように指示すると、エディターはファイルへの書き込みを許可するために一時的に権限を緩和します。
...
stat("test-file", {st_mode=S_IFREG|0444, st_size=7, ...}) = 0
getuid() = 1000
chmod("test-file", 0100644) = 0
...
open("test-file", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 4
write(4, "I am good singer,\n", 18) = 18
fsync(4) = 0
close(4) = 0
chmod("test-file", 0100444) = 0
....
その場合、iノード番号は変更されません。
最後に、これはバグではありません。ファイルの権限を変更することが許可されていない場合、vi
を使用して変更することはできません。