第 5 章 · 03 目录权限的真相: 、 、 各是什么 本节摘要:这是本章最反直觉、也最该吃透的一节。多数人把目录的 / / 当成「读目录、改目录、执行目录」——前两个勉强对,第三个完全错。目录的 不是「执行」,而是「进入」(能 进去、能访问里面文件的 inode)。这一个误解,导致了 Linux 里最经典的两个怪现象:「我能 出文件名,却 不了文件内容」(有 没 ),以及「我只是给了目录写权限,你怎么把我文件删了?」(目录 等于送人增删权)。本节要把目录三种权限的真实含义、它们与文件权限的对照、以及由此衍生出的「粘滞位」机制讲透,让你从此对目录权限心如明镜。 内容来源:原项目「Linux 命令大全」 与 ,精选并套用体系化模板。
r、w、x 各是什么本节摘要:这是本章最反直觉、也最该吃透的一节。多数人把目录的
r/w/x当成「读目录、改目录、执行目录」——前两个勉强对,第三个完全错。目录的x不是「执行」,而是「进入」(能cd进去、能访问里面文件的 inode)。这一个误解,导致了 Linux 里最经典的两个怪现象:「我能ls出文件名,却cat不了文件内容」(有r没x),以及**「我只是给了目录写权限,你怎么把我文件删了?」**(目录w等于送人增删权)。本节要把目录三种权限的真实含义、它们与文件权限的对照、以及由此衍生出的「粘滞位」机制讲透,让你从此对目录权限心如明镜。
内容来源:原项目「Linux 命令大全」
command/chmod.md与command/ls.md,精选并套用体系化模板。
阅读完本节,你应当能够:
r=列出文件名、w=增删/重命名文件、x=进入目录与访问 inode。ls 却打不开文件」的根因:目录有 r 无 x,能列名但访问不到 inode。w 等于送人删除权」:删文件改的是目录,不是文件本身。+t,1777)保护共享目录(如 /tmp),让每个人只能删自己的文件。700(私人)、755(公开只读)、1777(共享可写)。Linux 里「一切皆文件」,目录也是文件——只不过目录文件的内容是「一张文件名到 inode 的映射表」。理解了这一点,目录权限的含义就豁然开朗:
文件名 → inode 编号关键概念:目录是一个「文件名 → inode」的映射表。
r让你能读这张表(列文件名),w让你能改这张表(增删文件),x让你能「遍历」这张表(用文件名去查 inode)。这就是为什么目录的x叫「traverse / search permission」,而不是「execute」。
把第 01 节的对照表展开,目录权限的精确含义是:
| 位 | 对普通文件 | 对目录 |
|---|---|---|
r |
读文件内容 | 列出目录里的文件名(ls 能列名) |
w |
改文件内容 | 在目录里增、删、重命名文件(改的是目录那张表) |
x |
执行文件 | 进入目录(cd)、根据文件名访问 inode(cat、stat) |
这三条里最反直觉的是目录的 w 和 x:
w 不是「改文件内容」,而是「增删文件」。改文件内容改的是文件本身的 inode,与目录无关;增删文件改的是目录那张表,需要目录的 w。x 不是「执行」,而是「穿过」。没有 x,你能看到目录里有哪些文件名(r),但无法用这些文件名去访问文件本身——因为「文件名 → inode」这一步查询被禁止了。ls 却打不开$ ls -ld shared/ drwxr--r-- user staff ... # 注意: 其他组是 r--, 没有 x $ whoami guest # 你是 guest, 属于「其他」 $ ls shared/ # 成功列出文件名(有 r) a.txt b.txt $ cat shared/a.txt # 失败! 没 x, 查不到 a.txt 的 inode cat: shared/a.txt: Permission denied
根因:ls shared/ 只需要目录的 r(读那张表,拿到文件名);但 cat shared/a.txt 需要目录的 x(用 a.txt 查到它的 inode,再访问文件)。其他组有 r 没 x,所以「能列名、打不开」。
w 等于送人删除权$ ls -ld shared/ drwxrwxrwx user staff ... # 其他组有 w(危险!) $ whoami guest $ rm shared/important.txt # guest 居然能删 user 的文件!
根因:删文件改的不是 important.txt 本身,而是 shared/ 这张表(把 important.txt 这条记录从表里抹掉)。只要目录有 w,任何有目录 w 的人都能删里面的文件,不管文件本身的权限是什么。这也是为什么 /tmp 用了粘滞位(下文讲)——不然大家互删对方的临时文件就乱套了。
⚠️ 注意:给目录
w是非常危险的操作。它意味着「任何能进这个目录的人都能增删里面的文件」,哪怕文件本身是只读的。共享目录必须配合粘滞位(+t)使用。
chmod 700 ~/.ssh # rwx------: 只有自己能进、能列、能改 chmod 700 ~/private # 同上
700(rwx------)是私人目录的标准权限——属主全权,其他人什么都没有(既不能 ls,也不能 cd,更不能访问里面的文件)。~/.ssh 必须是 700,否则 SSH 会拒绝使用你的私钥。
chmod 755 ~/public_html # rwxr-xr-x: 自己可写, 别人能进能读不能改
755 让别人能 cd 进去、能 ls、能读里面的文件,但不能增删改。这是 web 服务器静态文件目录的典型权限。
chmod 1777 /tmp # rwxrwxrwt: 所有人都能写, 但粘滞位保护 ls -ld /tmp # drwxrwxrwt root root ... # 最后那个 t 就是粘滞位
1777 里的 1 是粘滞位(sticky bit),显示为目录最右位的 t。它的作用是:在这个可写目录里,每个人只能删自己创建的文件(root 例外)。这就是 /tmp 的设计——大家都需要往里写临时文件,但不能互删。
💡 技巧:粘滞位用
chmod +t dir或chmod 1xxx dir设置。任何「多人可写」的共享目录都应该加粘滞位,这是基本的安全卫生。
chmod 711 ~/secret # rwx--x--x: 别人能 cd 进去, 但 ls 不出文件名
711(rwx--x--x)让其他人只有 x 没有 r——他们能 cd 进去,也能访问已知文件名的文件,但 ls 列不出目录里有什么。这是一种「隐蔽目录」:你不告诉别人里面有什么,他们就看不出来,但如果你直接告诉他们文件名,他们还是能打开。
要访问一个文件,你不仅要对该文件有权限,还要对路径上所有目录有 x。
# 假设: # /home/user/ 权限 755 (其他有 x, 能穿过) # /home/user/data/ 权限 700 (其他无任何权限) # /home/user/data/secret.txt 权限 644 (其他有 r) $ cat /home/user/data/secret.txt cat: /home/user/data/secret.txt: Permission denied
虽然 secret.txt 本身是 644(其他可读),但 /home/user/data/ 是 700,你穿不过去(没 x),所以根本到不了 secret.txt。目录权限是「路径上的关卡」,任一关卡过不去就到不了文件。
这个「双重门槛」性质有个重要推论:「能不能访问一个文件」不取决于文件本身权限,而取决于「路径上所有目录的 x + 文件本身的相关权限」。这就是为什么把敏感文件放进 700 目录比依赖文件本身的权限更稳——多一道关卡。
实际项目里常有「多人协作,但权限分层」的需求。典型设计:
/srv/project/ 权限 2770 (sgid 位, 见下) ├── docs/ 公共文档, 所有人可读 ├── src/ 代码, dev 组可写 └── secrets/ 密钥, 只有 ops 组能读
sudo mkdir -p /srv/project/{docs,src,secrets} sudo chown -R admin:dev /srv/project sudo chmod 2770 /srv/project # 2770: sgid 位, 新建文件自动继承 dev 组 sudo chmod 2775 /srv/project/docs # docs 让所有人可读 sudo chmod 2770 /srv/project/src # src 只 dev 组 sudo chmod 2700 /srv/project/secrets # secrets 只 admin
这里用到了 2xxx(sgid 位):目录设了 sgid 后,新建文件自动继承目录的属组,而不是创建者的主组。这样 dev 组成员在 src/ 下创建的文件,属组自动是 dev,组内其他人就能改——这是组协作的标准模式。
关键概念:sgid 位(
2xxx)是组协作的关键——它让「目录里新建文件的属组」自动等于「目录的属组」,而不是创建者的主组。配合chmod 2770,组内所有人都能读写,无需手动chgrp。
sudo -u 切换视角设计完权限后,怎么验证「别人能不能访问」?用 sudo -u 切到目标用户视角测试:
# 你是 admin, 想测试 bob 能不能读 /srv/project/docs/readme sudo -u bob cat /srv/project/docs/readme # 如果输出内容, 说明 bob 能读; 如果 Permission denied, 说明权限设计有漏
这比「自己脑内推演权限」可靠得多——实际切到对方身份跑一遍,是最稳的验证方式。生产环境改完关键目录权限后,务必用 sudo -u 测一遍各角色。
chmod 444 important.txt # 文件设成只读 rm important.txt # 居然能删! (如果有目录 w)
很多人以为 444(只读)能防止文件被删,完全错。删文件不需要文件本身的 w,只需要所在目录的 w。要防删,得去掉目录的 w,或加粘滞位。文件本身的权限只管「能不能改内容」「能不能读内容」,不管「能不能被删」。
r 却不给 x,导致各种奇怪报错新手常给目录设成 chmod 644 dir(rw-r--r--),心想「让大家能读」。结果:
$ ls dir/ # 居然能列文件名(有 r) $ cd dir/ # 失败! 没 x cd: dir: Permission denied $ ls -l dir/ # 能列名, 但显示不出详细信息(因为查不到 inode)
目录缺 x 是「半残」状态:能列名但用不了。正常使用的目录至少要有 r-x,绝不能只给 r。
/tmp 改成 777 后大家互删chmod 777 /tmp # 去掉了粘滞位! 任何用户都能删任何文件
/tmp 默认是 1777(带粘滞位)。如果你手滑改成 777,粘滞位没了,所有用户都能删别人的临时文件,系统很快会乱套。修复:马上 chmod 1777 /tmp。
chmod -R 改了目录 x 导致打不开第 02 节提过,chmod -R 644 somedir/ 会把目录的 x 也改没,导致所有子目录都进不去。目录必须保留 x,递归改权限时务必文件/目录分开处理。
粘滞位规则是「普通用户只能删自己的文件」,但 root 不受限——root 想删谁的就删谁的。所以粘滞位保护的是「用户之间互相伤害」,不是「防 root」。要防 root 删,得靠备份,而不是权限位。
mv 跨目录移动文件受目标目录 w 限制$ ls -ld /srv/incoming drwxr-xr-x admin dev ... # 其他只有 r-x, 没 w $ mv myfile /srv/incoming/ mv: cannot move 'myfile' to '/srv/incoming/myfile': Permission denied
很多人纳闷:「我有文件本身的 w,为什么 mv 失败?」——因为 mv 到目标目录,等于在目标目录「新建条目」,需要目标目录的 w。同时,源目录也需要 w(因为要从源目录表里删条目)。所以 mv 需要「源目录 w + 目标目录 w + 文件本身权限」三者配合。
💡 技巧:记住三句话总结:读文件看文件
r,改文件看文件w,删/移文件看目录w。这三条覆盖了 90% 的目录权限困惑。
find/grep -r 漏扫$ grep -r 'error' /var/log/ 2>/dev/null # 某些子目录扫不到, 静默跳过
如果 /var/log/ 下某个子目录权限是 700,而你不是属主,grep -r 想「进入」它就需要 x,「列出文件」需要 r——都没有就只能跳过,而且加 2>/dev/null 后连报错都看不到。排查「为什么 find/grep -r 漏了某些目录」,先看那些目录的权限是不是太严。这也是为什么这类扫描命令常配合 sudo 使用。
x 权限被误改后 web 服务整站打不开sudo chmod -R 644 /var/www/html # 想把文件都设成只读 # 结果整站 500! 因为目录的 x 也没了
这是第 02 节「-R 陷阱」在目录权限层面的典型发作:目录失去 x 后,web 服务器(以 www-data 身份)进不去任何子目录。浏览器访问 http://site/css/style.css,web 服务器要穿过 /var/www/html/css 一串目录,任何一个缺 x 都到不了文件。
修复:立即把目录的 x 加回去:
sudo find /var/www/html -type d -exec chmod 755 {} \; sudo find /var/www/html -type f -exec chmod 644 {} \;
关键概念:web 服务「打不开整站」但单个文件权限看着没问题,十有八九是某个父目录的
x丢了。这是为什么第 02 节反复强调「目录和文件分开chmod」——目录必须有x,这是非 negotiable 的。
# /shared 是 1777, 你和同事都在里面放文件 $ ls /shared yourfile.txt coworker_file.txt $ rm /shared/coworker_file.txt rm: cannot remove '/shared/coworker_file.txt': Operation not permitted
这是粘滞位正常工作的表现——你删不掉同事的文件。新手可能以为「我也有目录 w,怎么删不了」,其实是粘滞位在保护。要清理共享目录里的他人文件,要么找文件主人,要么用 root。
💡 技巧:粘滞位是「双向保护」——它保护你,也保护别人不被你删。设计共享目录时,粘滞位是默认该开的;但也要提前告诉团队成员「只能删自己的」,避免困惑。
r = 列出文件名(ls 能列);w = 增删/重命名文件(改目录那张表);x = 进入目录、根据文件名查 inode(cd、访问文件)。ls 却 cat 不了」:目录有 r 没 x,能列名但查不到 inode——正常目录必须 r-x 起步,不能只给 r。w 等于送人删除权」:删文件改的是目录那张表,与文件本身权限无关。文件 444 也照样能被删,只要所在目录有 w。+t(1777):让可写目录里每个人只能删自己的文件。所有多人可写目录(如 /tmp)都必须加粘滞位。2xxx):让目录里新建文件自动继承目录的属组,组协作的标准模式。x 才能访问到目标文件——目录权限是「关卡」,任一关卡不通就到不了;这也是把敏感文件放进 700 目录比依赖文件本身权限更稳的原因。700(私人)、755(公开只读)、711(隐蔽,能进不能列名)、1777(共享可写+粘滞位)、2770(组协作+sgid)。下一节把视角从「权限」抬到「身份」:chown 和 chgrp。权限位决定了「能或不能」,而身份决定了「你是属主、属组、还是其他」——后者是前者生效的前提。我们还要讲清 Linux 权限检查的顺序:「先属主→属组→其他,命中即止」,这正是为什么属主权限有时反而比其他小。