Skip to content

为什么新建的文件权限是 000 ​

这个坑很邪门:文件明明建出来了,ls 也能看到,名字、大小、时间都对,但打不开。

$ ls -l note.md
---------- 1 app app 4521 Oct 23 07:12 note.md

十个横杠。所有者自己都读不了。

什么时候会遇到 ​

我这边的触发场景是:一个跑在容器/沙箱里的进程(身份是独立用户),往宿主机的一个普通目录里写文件。

写完 ls -l 一看,----------。之后任何操作都失败:

$ cat note.md
cat: note.md: Permission denied
$ chmod 644 note.md
chmod: changing permissions of 'note.md': Operation not permitted   # 如果连所有者都不是你

最坑的是它不报错。写的时候没有任何警告,等你三步之后想 git init、想重新编辑、想让别的进程读的时候才发现。

原因:umask ​

umask 是"创建文件时要抹掉哪些权限位"的掩码。

正常 shell 里 umask 是 022:

默认文件权限 0666 & ~022 = 0644  →  rw-r--r--

如果某个进程的 umask 是 0777(或者干脆是 0,取决于语言/框架怎么算),算出来就是:

0666 & ~0777 = 0000  →  ----------

为什么会有进程用 0777? 常见原因:为了「安全默认」把新建文件设成只有自己(甚至自己也不给)能访问,等显式 chmod 再放开。如果代码路径里少了那一步,文件就永远停在 000。

怎么确认 ​

bash
# 看当前 shell 的 umask
umask

# 看某个进程的 umask(Linux)
grep -i umask /proc/<pid>/status

怎么修 ​

1. 补一次权限(最直接)

bash
chmod -R u+rwX,go+rX .

为什么用 u+rwX 而不是 u+rwx?大写 X 的意思是「只对目录、以及已经有执行位的文件加 x」。这样目录能进得去,普通文本文件不会莫名其妙变成可执行。

2. 顺手把 umask 设回去

bash
umask 022      # 当前 shell 生效
# 或写进 ~/.bashrc / 服务单元的 UMask=0022

3. docker cp 出来的文件也要修

docker cp 保留容器内的属主/权限。如果容器里那个文件就是 000(或者属主是容器里的 uid,宿主机上不存在这个用户),拷出来照样打不开。

bash
docker cp container:/app/config.php ./config.php
chmod 644 ./config.php

一个判断技巧 ​

ls -ld <目录> 看到:

d--------- 2 app app 4096 ...

就知道整个目录都是这个状态 —— 这时候 cd 都进不去,得先 chmod 目录本身。

教训 ​

这件事本质上是**"不报错的失败"**里最典型的一类:命令成功、文件存在、大小非零,唯一的问题是它的元数据不对。

所以现在的习惯是:跨权限边界写文件之后,不要只看"文件在不在",要看 ls -l 的第一列。 尤其是自动化脚本,我会在写完后加一句:

bash
[ -r output.json ] || { echo "文件不可读,检查权限/umask"; exit 1; }

比等到十步之后报 Permission denied 强得多。

一句话版本:新建文件不可读,先看 umask,再 chmod -R u+rwX,go+rX .;docker cp 出来的别忘了补 chmod 644。