构建 Permission denied 报错速查
更新时间:2026-10-24。FAQ 速查页,面向在 Linux 上用 make/CMake 构建工程、被权限问题卡住的同学。Permission denied 是构建期权限类报错的总开关:make 想建目录、想写
.o、想执行脚本、想 install 到系统路径,任何一步没有权限都是这一句。报错的麻烦在于它只告诉你「不行」,不告诉你「为什么不行」——是之前用 sudo 构建过、build 目录属主变成了 root,是文件系统只读挂载,是 umask 把目录权限掐死了,是 install 目标写/usr/local本来就该要权限,还是构建目录建在了没权限的受限路径下。本篇以报错原文为锚,六根轴顺一遍基本必中。如果报错不是权限而是规则找不到,请转 make: No rule to make target 报错速查。
本文要回答的问题
- 昨天还能编,今天突然满屏 Permission denied,最可能是什么原因?
ls -l里哪些字段是排查权限的关键?- 只读挂载的报错和普通权限不够怎么区分?
make install写/usr/local失败,该不该直接 sudo?- 脚本文件存在却报 Permission denied,是什么在作怪?
一、报错原文与典型现象
最典型的是写构建产物被拒(工程路径以 /proj/demo 占位):
$ make
mkdir -p /proj/demo/build
mkdir: cannot create directory '/proj/demo/build': Permission denied
make: *** [Makefile:10: build] Error 1或者目录能建、文件写不进:
cc -c -o build/main.o src/main.c
Assembler messages:
Fatal error: can't create build/main.o: Permission deniedinstall 阶段的版本:
$ make install
/usr/bin/install -c app /usr/local/bin
/usr/bin/install: cannot create regular file '/usr/local/bin/app': Permission denied
make: *** [Makefile:30: install] Error 1读懂这类报错抓两件东西:被拒的路径(build/main.o 还是 /usr/local/bin/app)和动作(建目录、写文件、还是执行)。路径在工程内部是属主/挂载问题,路径在系统目录是 install 权限设计问题,两者修法完全不同。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| 突然全库 Permission denied | 之前 sudo make 过,build 目录属主是 root | ls -ld /proj/demo/build 看属主 | sudo chown -R $USER:$USER /proj/demo |
| 工程目录整体写不进 | 文件系统只读挂载(ro) | mount | grep 挂载点 看是否 ro | 换可写目录,或查清为何被 ro 重挂 |
| 新同事/新用户首次构建就拒 | 目录属组与权限位不含写 | ls -ld 看组与 drwx 位,id 看自己在哪个组 | 加组或 chmod g+w,约定 umask 002 |
| 报错路径在 /usr/local 或 /usr | install 目标写系统目录,设计上就要提权 | 看 makefile install 规则的 PREFIX | sudo make install,或 make install PREFIX=~/.local |
| 执行构建脚本报 Permission denied | 脚本缺可执行位 | ls -l script.sh 看 x 位 | chmod +x script.sh,或 bash script.sh 调用 |
| 构建目录建在 /root、他人家目录 | 选了无权路径 | ls -ld 路径每一级查 x 位 | 换到自己有权限的目录重建构建目录 |
| 报错出现在容器/CI 里 | 容器内外 UID 不一致,卷挂载属主错位 | 容器内 id 对比宿主机文件属主 | 统一 UID 构建,或容器内 chown 后构建 |
| 删不掉旧产物导致重写失败 | 旧文件属主不是自己且无写权限 | ls -l build/ 逐文件看 | sudo rm -rf build 后用自己的身份重建 |
三、排查决策图
下面这张图是按「被拒路径在哪」分岔的标准路线,颜色按角色区分:红是报错入口与命中的根因,蓝是排查动作,绿是通过分支,橙是修复动作,紫是防复发沉淀。
读这张图抓住一个分水岭:先看报错里被拒的路径归谁管。工程内部路径(左支)是属主、挂载、umask 的事实问题,chown/chmod 就能解;系统目录(右支)是 install 的设计行为,要么名正言顺 sudo make install,要么把 PREFIX 改到自己家目录下免提权。混着修最常见的翻车是 sudo 构建整个工程,反而制造下一批 Permission denied。
四、逐条排查展开
第一步:ls -ld 把被拒路径的属主和权限位打出来。 这是权限排查的万用表,注意 -d 看目录本身而不是目录内容:
ls -ld /proj/demo/build
# drwxr-xr-x 3 root root 4096 Oct 24 09:12 /proj/demo/build
id
# uid=1000(dev) gid=1000(dev) groups=1000(dev)属主是 root 而你是普通用户——这就是「sudo 构建残留」的标准画像:某次手滑 sudo make,build 目录连同里面的 .o 全成了 root 的,之后普通身份再构建,写任何东西都被拒。修法是把属主还给自己:
sudo chown -R "$USER:$USER" /proj/demo更稳妥的做法是 sudo rm -rf build 清掉整个残留,普通身份重建。根治只有一条纪律:工程目录里永远不用 sudo 跑 make——sudo 只配出现在 make install 那一步。
只读挂载是另一类「突然全拒」。 磁盘故障重挂 ro、NFS 导出为只读、容器卷只读挂载,都会让原本可写的目录一夜之间全是 Permission denied。区分办法是看挂载表:
mount | grep proj
# /dev/sda1 on /proj type ext4 (ro,relatime)括号里是 ro 就坐实了只读挂载——这不是 chmod 能修的,要么查清为什么被重挂成只读(ext4 出错内核会自动 ro 重挂,查 dmesg),要么把工程挪到可写文件系统。
install 写系统目录是设计内行为,别乱修。 /usr/local/bin 属主 root、权限 drwxr-xr-x,普通用户写不进去天经地义。三条正路:需要装到系统路径就 sudo make install(只提权这一步);不需要系统级安装就把前缀改到家目录,make install PREFIX="$HOME/.local" 或 CMake 工程 cmake --install build --prefix "$HOME/.local",再把 ~/.local/bin 加进 PATH;打包场景用 make install DESTDIR=/tmp/stage 装到暂存目录。最差的选择是 sudo make 全量构建——那会把 build 目录属主污染成 root,给下一次构建埋雷。
脚本缺可执行位是最便宜的一种。 makefile 里调用 ./gen_code.sh 而文件只有 rw-r--r--,报的就是 Permission denied 而非 command not found。chmod +x 修之;文件在版本库里没保留 x 位时,用 git update-index --chmod=+x 补上。不想改权限位,把调用改成 bash gen_code.sh 显式走解释器也行。
容器与 CI 里的 UID 错位越来越常见。 Docker 容器里默认 root 构建、挂载出来的 build 目录属主就是 root,宿主机普通用户接着编立刻 Permission denied;反过来容器内用固定 UID(比如 1000)而宿主机用户是 1001,产物同样写不进。处置是构建镜像里按宿主机 UID 建用户(--build-arg UID=$(id -u)),或容器内构建完统一 chown 再退出。
路径每一级都要查。 Unix 权限是逐目录检查的:/proj/demo 你可写,但 /proj 本身没有你的 x 位,照样进不去。从被拒路径向上逐级 ls -ld,找到第一个卡住你的层级。
五、规范做法与预防
- sudo 只给 install:工程目录内的构建动作一律普通身份,sudo 是 install 写系统路径的专属。
- 构建目录独立于源码树:out-of-tree 构建(
cmake -S . -B build),出问题整个删掉重来,不污染源码。 - 团队约定 umask 002:多人共用构建机时,组可写能省掉大批属组问题。
- 容器构建统一 UID:Docker 构建镜像里用与宿主机一致的 UID 建用户,卷挂载产物属主不打架。
- 脚本入库存 x 位:构建辅助脚本提交时保留可执行位,或 makefile 里统一
bash xxx.sh调用。
这一篇在知识体系里的位置
本篇是 Makefile 排错速查簇的权限篇,定位「构建动作被文件系统拒绝」:权限与属主问题看本篇;规则展开失败的看 make: No rule to make target 报错速查;CMake 配置阶段版本/策略问题的看 CMake 版本不兼容报错速查;配置阶段连编译器都找不到的看 CMake 找不到编译器速查;头文件找不到的看 fatal error: xxx.h 速查;链接阶段炸符号的看 undefined reference 链接错误速查。ls -ld、mount、id 三件排查套在所有权限类报错间完全通用。
一句话总结
构建 Permission denied 先按被拒路径分侧:工程内部路径用 ls -ld 查属主(sudo 构建残留就 chown 回自己或删 build 重建)、用 mount 查只读挂载、逐级目录查权限位;系统目录路径是 install 的设计行为,只提权 sudo make install 或把 PREFIX 指到家目录;脚本报这条先补可执行位。纪律上工程目录永不 sudo 构建、install 单独提权、out-of-tree 构建随时可整删重来,这条报错即可基本绝迹。