尽管文件是只读的,但 Vi 可以写入文件

mai*_*ash 12 linux vi ls chmod

以下示例显示如何创建仅具有读取权限的文件。正如我们所见,当我尝试使用 echo 命令写入该文件时,我得到了Permission denied.

但是为什么,在我们使用 vi 的情况下,我们没有得到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
Run Code Online (Sandbox Code Playgroud)

Ant*_*gan 29

注意:由于遗留许可的原因,大多数 GNU/Linux 发行版不包含 Bill Joy 编写的原始 vi 程序。相反,vi 命令是通过在 vi 兼容模式下运行 Vim 来提供的。以下答案基于运行 Vim 及其 vi 兼容模式。

修改只读文件

如果用户修改了只读文件的缓冲区,Vim 会警告用户W10: Warning: Changing a readonly file。如果用户尝试写入此文件,则会收到以下错误消息'readonly' option is set (add ! to override)

当 Vim 用户可写父目录时

Vim 很有帮助,让用户知道他们可以通过!w命令后附加感叹号来强行坚持写作。如果使用这个强制版本的 write 命令,Vim 会删除原始文件(如果使用 Vim 并设置了 Vim-onlybackup选项,则原始文件实际上被重命名为与备份文件相同)。然后它打开(创建)一个与原始文件同名新文件,并将其缓冲区的内容写入这个新文件。这可以通过在运行 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
Run Code Online (Sandbox Code Playgroud)

注意:这也可能改变文件的权限和所有权并破坏(符号)链接,例如,如果原始文件为另一个用户所有,新文件将由运行 Vim 的用户所有。

如果进程对文件的父目录具有写权限,则它只能执行此操作。一般来说,为了确保程序不能修改文件,文件本身及其父目录的权限都应该得到保护。

当 Vim 用户无法写入父目录时

但是,即使在这种情况下,Vim 仍然会尽力帮助坚持的用户覆盖文件。如果 Vim 用户拥有文件的所有权,Vim 可以通过临时更改文件的权限(使用chmod系统调用)、将缓冲区写入文件、关闭文件然后更改权限回来。以下是通过 strace 运行 vi 时进行的系统调用的摘录strace -o ../vi.trace vi t

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
Run Code Online (Sandbox Code Playgroud)

注意:如果 Vim 用户正在编辑一个他们没有所有权的文件,则不会发生这种情况,因为 Vim 将无法更改文件权限。

附录

要真正确定不能修改文件(在 GNU/Linux 系统上),请chattr以超级用户身份运行命令:

sudo chattr +i filename
Run Code Online (Sandbox Code Playgroud)

来自man chattr

不能修改具有“i”属性的文件:不能删除或重命名它,不能创建指向该文件的链接,也不能将数据写入该文件。只有超级用户或拥有 CAP_LINUX_IMMUTABLE 能力的进程才能设置或清除此属性。

  • @CamilleGoudeseune 在我发布了我的答案的第一个版本后,我做了一些实验,最终花了大约一个小时通过 strace 运行 Vim,以查看它在不同情况下的幕后做了什么(文件和所有权的不同排列)父目录)。我有时会得意忘形,但是一旦我发表了答案,我就想确保我所说的是正确的。 (4认同)
  • 圣烟,太彻底了! (2认同)

jll*_*gre 5

vi如果您使用诸如ZZ, :w,:wq或 之类的常规保存命令,则大多数(如果不是全部)实现都会阻止您写入文件:x,例如vim

:w
E45: 'readonly' option is set (add ! to override)
Run Code Online (Sandbox Code Playgroud)

另一方面,如果您vi不顾文件的权限而告诉它写入文件,使用类似:x!或 的东西:wq!,编辑器会暂时放宽权限以允许写入文件:

...
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
....
Run Code Online (Sandbox Code Playgroud)

在这种情况下,inode 编号保持不变。

最后,这不是一个错误,就像您不允许更改文件权限一样,您无法通过vi.