代码之家  ›  专栏  ›  技术社区  ›  Daniel Papasian

为什么rename()系统调用禁止将无法写入的目录移动到其他目录?

  •  1
  • Daniel Papasian  · 技术社区  · 14 年前

    我试图理解为什么这个设计决策是用4.2bsd中的rename()syscall做出的。我在这里没有试图解决的问题,只是理解行为本身的基本原理。

    4.2bsd引入了rename()syscall,以允许文件的原子重命名/移动。从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创建新链接和更改权限,我可以非原子地实现相同的效果。(作为练习留给读者)

    . 并且…已经有特殊的大小写了,所以我并不特别相信,如果我不能写一个目录,我就不能“改变…”这是很直观的,这就是源代码所建议的。这除了是代码作者认为正确的行为之外,还有什么原因吗?如果我们让人们原子化地在他们可以写的目录之间移动目录(他们不能写),会有什么不好的事情发生吗?

    3 回复  |  直到 14 年前
        1
  •  2
  •   Keith Smith    14 年前

    我认为安德鲁·麦格雷戈很可能是对的。不一定是不明飞行物 以这种方式工作,但是实现者(kirk mckusick)只是扩展了文件系统权限的逻辑来覆盖这种情况。因此,如果您对目标目录没有写权限,就不能更改其“.”项。

    但在看你的例子时,我想到了另一种可能性。这可能不是像您所展示的那样,一个用户拥有所有相关目录的情况,而是一个目录由不同用户拥有的情况。换句话说,此检查阻止我在具有写权限的父目录之间移动您拥有的目录。当然,假设您没有在您的目录中授予我写权限。

    诚然,在正常使用情况下,出现这种情况的可能性非常小。但是内核必须担心所有古怪的用例以及常见的用例。

    一个明显的反论点是,如果我们想担心这种情况,那么我们还想阻止人们在他们有写权限的目录之间移动他们不拥有的文件…

        2
  •  0
  •   Andrew McGregor    14 年前

    Also the user must have write permission in the source so as to be able to change "..".

    换句话说,为了使目录在移动后格式良好,您必须更改..链接在里面,你没有权限这样做。所以这只是权限方案的一个逻辑部分,尽管这不是一个非常明显的部分。

        3
  •  0
  •   mark4o    14 年前

    存在许多专门的程序,这些程序允许非特权用户在某些狭义的条件下执行某些通常具有特权的操作。此类程序通常使用setuid标志工作。在程序中,它检查以确保满足特殊条件,如果满足,则执行特权操作。

    有时需要按名称引用文件,例如,如果要执行以文件名为参数的程序。如果必须首先执行检查,这可能导致危险的竞争条件,如果没有权限的用户可以在检查时间和使用时间之间重命名路径名的任何部分。有时,这可以通过要求包含每个指定路径组件的目录对未被授予权限的用户没有写权限来解决,确保在此期间未被授予权限的用户不能重命名(或取消链接并重新创建)路径组件以引用与检查了什么。如果没有权限的用户可以更改“.”所指的内容,即使没有该目录中的写入权限,也会造成安全漏洞。如果这是内核允许的特殊异常,则进行这种检查的每个程序都必须专门检查“.”组件以避免此问题。

    此外,更新一个目录中的“.”需要写入该目录的数据块,并且还将更新该目录的最后修改时间,至少在传统的unix和bsd文件系统上是这样的(我知道这不是apple hfs+,其中“.”是合成的)。关闭其他用户的写权限似乎很直观,这将禁止他们对目录进行写操作或更改其上次修改的时间。