我试图理解为什么这个设计决定是使用 4.2BSD 中的 rename() 系统调用做出的。我在这里没有要解决的问题,只需了解行为本身的基本原理即可。
4.2BSD 引入了 rename() 系统调用,目的是允许对文件进行原子重命名/移动。从 4.3BSD-Reno/src/sys/ufs/ufs_vnops.c:
/*
* If ".." must be changed (ie the directory gets a new
* parent) then the source directory must not be in the
* directory heirarchy above the target, as this would
* orphan everything below the source directory. Also
* the user must have write permission in the source so
* as to be able to change "..". We must repeat the call
* to namei, as the parent directory is unlocked by the
* call to checkpath().
*/
if (oldparent != dp->i_number)
newparent = dp->i_number;
if (doingdirectory && newparent) {
VOP_LOCK(fndp->ni_vp);
error = ufs_access(fndp->ni_vp, VWRITE, tndp->ni_cred);
VOP_UNLOCK(fndp->ni_vp);
很明显,这个检查是故意添加的。我的问题是——为什么?这种行为应该是直观的吗?
这样做的效果是不能将一个不能写入的目录(位于一个可以写入的目录中)移动到另一个可以原子写入的目录。但是,您可以创建一个新目录,移动链接(假设一个人对该目录具有读取权限),然后删除一个人对该目录的写入位。你不能原子地这样做。
% cd /tmp
% mkdir stackoverflow-question
% cd stackoverflow-question
% mkdir directory-1
% mkdir directory-2
% mkdir directory-1/directory-i-cant-write
% echo "foo" > directory-1/directory-i-cant-write/contents
% chmod 000 directory-1/directory-i-cant-write/contents
% chmod 000 directory-1/directory-i-cant-write
% mv directory-1/directory-i-cant-write directory-2
mv: rename directory-1/directory-i-cant-write to directory-2/directory-i-cant-write: Permission denied
我们现在有一个我无法写入的目录,其中包含我无法读取的内容,我无法以原子方式移动。但是,我可以通过更改权限、创建新目录、使用 ln 创建新链接以及更改权限来非原子地实现相同的效果。(留给读者作为练习)
. 和 .. 已经是特殊情况了,所以我并不特别相信,如果我不能写一个目录,我就不能“改变 ..”,这就是消息来源所暗示的。除了代码作者认为正确的行为之外,还有什么理由吗?如果我们让人们在他们可以写的目录之间原子地移动目录(他们不能写),会发生什么不好的事情吗?