用户权限究竟决定了什么
说起文件的权限,这绝对是个值的单独开个系列好好说的话题。不过,好在我们并不是在讲操作系统的内核,本着恰到好处的原则,这一节,我们和大家分享一些最重要的和权限有关的话题。
围绕着文件权限,系统中有两个对象和它有关系,分别是:账号和进程。其中,它决定了前者可以进行的操作;也决定了后者在执行时的角色。我们先从大家熟悉的部分开始。
决定账号可以进行的操作
无论是iOS还是Mac的终端,在任意一个目录中,当我们执行ls -l
的时候,就可以看到类似这样的结果。其中,前4列是我们主要关心的内容:

第一列形如drwxr-xr-x
的部分,就是这个文件的权限,它分成四部分,分别是:d rwx r-x r-x
。这里,从左到右:第一位表示文件的类型:
-
d
表示目录; -
-
表示普通文件; -
l
表示符号连接;
在上面的截图中,这三种类型我们都展示出来了。
接下来的三组:rwx r-x r-x
,表示系统中三类不同角色的人可以对这个文件进行的操作。这三类角色,分别是:
- 文件属主(User);
- 文件所属用户组(Group);
- 其它人(Other);
通常,我们会用U / G / O
来表示这三类角色。注意,这里,我们使用了单词User和字母U表示属主,而没有使用Owner,这主要是为了和Other这类角色区分开,否则开头字母都是O就容易混淆了。并且,在C API中,用于描述文件属主权限的一些宏,例如:S_IRUSR / S_IWUSR / S_IXUSR
也同样使用了User来表示属主,大家要注意这个事情。
了解完角色之后,我们再来看它们可以进行的操作。这部分很简单,分别是:
-
r
:读权限; -
w
:写权限; -
x
:可执行权限; -
-
:没有权限;
于是,把三类角色和三种不同的操作组合起来,这里我们一共有九种不同的情况,具体到上面的例子:
-
rwx
对应属主的权限,表示属主对这个文件可读、可写、可执行; - 中间的
r-x
对应所属用户组的权限,表示可读、不可写、可执行; - 最后的
r-x
对应其它人的权限,它和用户组的权限是一样的;
和目录有关的权限
接下来,我们着重说说目录权限的话题。rwx
对普通文件来说很好理解,但是,对于一个目录来说,读、写和执行都意味着什么呢?我们知道,本质上,目录其实也是一个文件,只不过这个文件的内容,是目录中包含的文件和子目录而已。例如,假设我们有下面这样的目录结构:

如果我们用vi直接打开parent目录,就会看到下面的内容:

看到了吧,parent这个目录文件里记录了4个内容,分别是:上级目录、当前目录、subdir和subfile。于是,目录的读权限就很好理解了,它允许我们读取目录文件的内容,也就是说可以使用ls
命令查看目录内容。
接下来,我们执行:chmod 300 parent
把属主的读权限去掉,当我们再查看这个目录内容的时候,就会拒绝访问了。而这,这就是目录读权限的含义。

但是要注意的是,不能读取目录的文件列表,不等于不能访问其中的文件。如果你知道这个目录下的确切文件名,可以直接访问它。例如,我们执行vi parent/subfile
就可以编辑文件了。这个操作的权限,是属于subfile自身控制的。
了解了目录的读权限之后,我们来看可执行权限。如果我们要真正访问一个文件,那么我们必须在这个文件的每一个路径上,具备可执行权限。例如,我们把parent的可执行权限去掉,就无法访问subfile了:

另外,对于我们刚才提到的文件的每一个路径,这里还要多说一句。所谓的每一个路径,只是基于路径字面值的,而不是实际的路径。
例如:如果我们要访问/home/bx11/a.conf
,那么,就需要在/
,/home
,/home/bx11
这三个目录上都有可执行权限。但是,如果当前目录是/home/bx11
,我们写成../a.conf
来访问,那么只要在/home/bx11
目录有可执行权限就好了,而不要求/
和/home
有可执行权限。
最后,我们来看写权限。所谓写,就是修改目录文件的内容。也就是说,只要有目录写权限,我们就可以添加或删除目录中的内容,而不需要我们对目录内的文件有任何权限。例如下面,尽管parent目录自身不可读,我们还是可以创建parent/subfile1。并且,去掉了subfile1的写权限之后,我们还是可以正常删掉它:

但要注意的是,实际上,在目录中添加删除文件,是要w
搭配x
才可以,没有可执行权限,我们是无法添加和删除文件的。
以上,就是我们在一开始提到的,文件权限对账号行为的影响。除此之外,它还决定了一个进程执行起来的角色。
决定进程在执行时的角色
当我们执行一个具有可执行权限的文件时,系统中就会多出来一个对应的进程。在终端里,我们执行ps au
就会看到类似下面的结果:

在其中的USER列中可以看到,有的进程,就是我当前登录的账号puretears,有的进程则是root。而这个角色的约定,也和文件的权限相关。为了理解这个问题,我们要先普及一些概念。
Real User ID & Real Group ID
首先,是Real User ID和Real Group ID,这两个值决定了我们在系统中的真实身份。我们可以在/etc/passwd
中的第三列和第四列内容中找到它们:

这个文件中记录了系统中所有账号的信息。可以看到,root的Real User ID和Real Group ID都是0,这个是固定的,而其它账号的这两个ID,则由系统在创建账号的时候生成。
Effective User ID & Effective Group ID
其次,是Effective User ID和Effective Group ID,这两个ID是进程的两个属性,决定了进程可以在系统中完成的操作(例如:是否可以调用某些API,访问某些系统资源等等)。通常情况下,这两个值和Real User ID以及Real Group ID是相等的。简单说,谁启动了进程,进程就代表谁工作。
Set-UID-program & Set-GID-program
但在有些时候,进程启动之后,是可以不代表当前用户的,而是代表应用程序文件的属主以及用户组。其中,最经典的例子,就是我们经常使用的sudo
。如果我们查看一下它的权限,就会发现是这样的:

看到了么?这次,文件属主的权限是r-s
,这里的s
也表示可执行,但是它表示这是一个Set-UID-program。也就是说,无论谁执行sudo
,sudo
进程的USER都是root,也就是一个特权进程。这也就是为什么我们可以通过sudo
完成一些系统级别操作的原因。
看到这,你可能会觉得,这是一个有点儿危险的举动。为了避免权限被滥用,非特权账号只能修改属主是自己的文件的Set-User-ID属性,也就是说,让别人以我们自己的身份运行某个程序。而修改的方法,就是使用chmod
命令,例如:
chmod u+s your_file
这样,your_file的x
权限,就变成s
了。
iOS中的两个账号
了解了这些和权限有关的知识之后,我们来看下iOS中的两个账号。一个是mobile
,所有通过AppStore安装的程序,都是以这个身份运行的,因此它们都会受到iOS沙箱机制的约束。例如,iOS中的Podcasts:


另一个,就是我们越狱之后使用的root
了,之前我们通过Cydia安装的iFile,就是以root
执行的,因此,它可以访问到iOS完整的文件系统:
