为什么文件重定向在 cp/mv 失败的情况下成功?

Why does file redirection succeed where cp/mv fail?

我正在尝试在 EKS 上覆盖 Docker 容器 运行 Alpine Linux 中的 /etc/resolv.conf。复制和移动均失败:

~ # cp /tmp/resolv.conf /etc/resolv.conf 
cp: can't create '/etc/resolv.conf': File exists
~ # mv /tmp/resolv.conf /etc/resolv.conf 
mv: can't rename '/tmp/resolv.conf': Resource busy

但是 shell 重定向成功,并保留了索引节点:

~ # ls -li /etc/resolv.conf 
1412461 -rw-r--r--    1 root     root           131 Feb 17 20:45 /etc/resolv.conf
~ # cat /tmp/resolv.conf.1386 > /etc/resolv.conf 
~ # ls -li /etc/resolv.conf 
1412461 -rw-r--r--    1 root     root            39 Feb 18 19:17 /etc/resolv.conf

为什么在繁忙的文件上重定向成功?

重定向不会更改文件所在的目录,而mv会。当您的 "directory entry" 实际上是一个安装点时,这是至关重要的,内核正在覆盖目录条目并且不让通常的内容暴露。

即:


mv foo bar 的作用

mv foo bar

...在操作系统级别,调用系统调用 rename("foo", "bar"),用目录所在的 inode 替换 目录条目 bar条目 foo 当前指向。它根本不会打开或更改先前存在的 bar 文件; 唯一它所做的事情是更改以前包含该文件的目录,而不是指向条目中具有相同名称的不同文件。


echo hello > bar 的作用

相比之下,echo hello >bar 根本没有更改 目录条目 ;它只是更改目录入口指向的文件。具体来说,就是 运行 open("bar", O_TRUNC)write(<fdno>, "hello\n")open() 返回的文件描述符。


那么,cp 在做什么?

...这是个好问题。 通常cp 就地修改现有文件。您可以按如下方式进行测试:

echo ORIGINAL > file1
ln file1 file2
echo REPLACED > file3
cp file3 file1
cat file2

如果 cp 用一个全新的文件替换 file1,那么 file2 关联的 inode 将保持不变(包含 "ORIGINAL")。但是,情况并非如此——file2cp 修改,后者仅直接将 file1 标识为其输出文件,将该文件更改为包含 REPLACED.

我需要查看 strace cp /tmp/resolv.conf /etc/resolv.conf 的输出来诊断所描述的行为。