在 FreeBSD 上,为什么非目录上的 chmod +t 是特权操作?

On FreeBSD, why is chmod +t on a non-directory a privileged operation?

在 FreeBSD 上,chmod +t <em>file</em> 是特权操作,如果 <em>file</em> 不是目录:

$ echo >test
$ chmod +t test
chmod: test: Inappropriate file type or format
$ su
Password:
# chmod +t test
# ls -l test
-rw-r--r-T  1 fuz  wheel  1 Jan 26 12:44 test

chmod(2) 记录此行为:

 [EFTYPE]           The effective user ID is not the super-user, the mode
                    includes the sticky bit (S_ISVTX), and path does not
                    refer to a directory.

同时,sticky(7) 解释说如果 <em>file</em>[=34,<code>S_ISVTX 将被忽略=] 不是目录:

 A special file mode, called the sticky bit (mode S_ISTXT), is used to
 indicate special treatment for directories.  It is ignored for regular
 files.  See chmod(2) or the file <sys/stat.h> for an explanation of file
 modes.

在 FreeBSD 上将非目录标记为粘滞是特权操作的历史或技术原因是什么?为什么在本该是 EPERMENOTDIR 的地方为此发明了一个额外的错误代码 EFTYPE

What is the historical or technical reason why marking a non-directory as sticky on FreeBSD is a privileged operation?

为什么不呢?首先,这显然不是一个有意义的操作,因此应该将其作为失败报告给用户,以表明他做错了什么。此外,允许它与任何未使用的标志或字段一样不是面向未来的证明 - 它可能会在未来用于某些事情,并且不会造成安全或一致性风险,它应该处于可预测的状态。

Why was an extra error code EFTYPE invented for this where it should have been EPERM or ENOTDIR?

参见错误号 (2)。 EFTYPE并不是专门为这种情况而发明的,具有更广泛的含义,比EPERM更适合这种情况,而ENOTDIR具有完全不相关的用途。

从历史上看,原始 Unix 不允许这样做。 V7 chmod man page 表示

Ability to set this bit is restricted to the super-user since swap space is consumed by the images

如果用户尝试设置粘性位,V7 chmod system call 不会 return 出错,但是,它只是默默地关闭该位:

if (u.u_uid)
    uap->fmode &= ~ISVTX;

此行为未更改地转移到 BSD。 4.3BSD 有这段代码,它为 non-root 用户悄悄关闭了 non-directories 上的 ISVTX 位:

if (u.u_uid) {
    if ((ip->i_mode & IFMT) != IFDIR)
        mode &= ~ISVTX;
    ...
}

代码已在 4.4BSD to return the error EFTYPE, although the 4.4BSD chmod man page 中更改,但未提及。不知道为什么改了。

if (cred->cr_uid) {
    if (vp->v_type != VDIR && (mode & ISVTX))
        return (EFTYPE);
    ...
}

FreeBSD 1.0 包含与 4.4BSD 相同的代码。

if (cred->cr_uid) {
    if (vp->v_type != VDIR && (mode & ISVTX))
         return (EFTYPE);
    ...
}

这已延续到当前版本。 FreeBSD 10.2 有这个:

if (vp->v_type != VDIR && (mode & S_ISTXT)) {
    if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0))
        return (EFTYPE);
}

OpenBSD 有相似的代码:

if (cred->cr_uid) {
    if (vp->v_type != VDIR && (mode & S_ISTXT))
        return (EFTYPE);
    ...
}

Solaris 10 默默地忽略 non-root 用户在文件上设置粘滞位的尝试。 (我会在可用时添加源代码片段。)