返回

Linux文件权限解析:S_IRUSR等宏定义详解

Linux

Linux 文件权限位解析:S_IRUSR 等宏定义的含义

查看文件权限时(比如用 stat()chmod() 函数),经常会碰到 S_IRUSR 这样的宏定义。例如,S_IRUSR 的值可能是 00400 (GNU/Linux)。 那么问题来了,这个 00400 到底是什么?它是数字吗,还是别的什么? 我知道怎么把这些宏定义用“或”运算组合起来,但就是不明白这些宏定义本身代表了什么。

一、 文件权限位的基本概念

Linux 的文件权限系统,控制着谁能对文件和目录进行哪些操作。 简单说,有三种基本操作:

  • 读 (r): 允许查看文件内容或列出目录内容。
  • 写 (w): 允许修改文件内容或在目录中创建/删除文件。
  • 执行 (x): 允许运行文件(如果是可执行文件)或进入目录 (cd)。

这三种权限可以分别授予三种用户类型:

  • 文件所有者 (user): 创建文件或目录的用户。
  • 用户组 (group): 文件所属的用户组中的成员。
  • 其他用户 (others): 既不是文件所有者,也不属于文件所属用户组的用户。

这样一来,一个文件或目录的权限,就可以用一个九位的二进制数表示,每三位分别代表所有者、用户组、其他用户的权限。比如:

rwxr-xr--  

上面这个权限表示:

  • 所有者 (rwx): 可读、可写、可执行。
  • 用户组 (r-x): 可读、可执行,不可写。
  • 其他用户 (r--): 只可读,不可写,不可执行。

二、 八进制表示法

上面这种 rwx 的表示方法虽然直观,但计算机内部实际上是用数字来表示权限的。 最常用的表示方法是八进制。

为什么是八进制? 因为每三位二进制数,正好可以用一位八进制数表示。 具体来说:

  • --- (000) -> 0
  • --x (001) -> 1
  • -w- (010) -> 2
  • -wx (011) -> 3
  • r-- (100) -> 4
  • r-x (101) -> 5
  • rw- (110) -> 6
  • rwx (111) -> 7

所以,前面那个 rwxr-xr-- 权限,用八进制表示就是 754

三、 权限位宏定义

S_IRUSRS_IWGRP 这些宏定义,其实就是对这些权限位的“命名”。它们定义在 <sys/stat.h> 头文件中(在某些系统里可能在其他头文件,可以用 man 2 stat 命令查看具体信息)。

回到最开始的问题,S_IRUSR 的值是 00400。 这个 00400 就是一个八进制数。 前面的两个 0 其实不影响值, 可以认为 00400 等同 0400. 可以拆解开理解:

  • 4: 对应二进制的 100,表示“读”权限。
  • 00: 这两个0 代表应用在所有者身上。
  • 第二个 0 代表对组没有任何权限设置。
  • 第三个 0 代表对其它用户没有任何权限设置。

所以,S_IRUSR 的含义就是:文件所有者的读权限。

下面列出一些常见的权限位宏定义及其对应的八进制值和含义:

宏定义 八进制值 含义
S_IRUSR 00400 文件所有者的读权限
S_IWUSR 00200 文件所有者的写权限
S_IXUSR 00100 文件所有者的执行权限
S_IRGRP 00040 用户组的读权限
S_IWGRP 00020 用户组的写权限
S_IXGRP 00010 用户组的执行权限
S_IROTH 00004 其他用户的读权限
S_IWOTH 00002 其他用户的写权限
S_IXOTH 00001 其他用户的执行权限
S_ISUID 04000 设置用户ID(SUID)
S_ISGID 02000 设置组ID (SGID)
S_ISVTX 01000 粘滞位 (sticky bit)

四、 使用方法举例

1. stat() 函数

stat() 函数可以获取文件的各种信息,包括权限。

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    struct stat sb;

    if (stat("my_file.txt", &sb) == -1) {
        perror("stat");
        return 1;
    }

    printf("File permissions: ");

    // 检查文件所有者的读权限
    if (sb.st_mode & S_IRUSR) {
        printf("r");
    } else {
        printf("-");
    }

    // 检查文件所有者的写权限
    if (sb.st_mode & S_IWUSR) {
        printf("w");
    } else {
        printf("-");
    }

   //... 其它检查

    printf("\n");

    return 0;
}

这段代码用 stat() 获取 "my_file.txt" 的信息,并检查文件所有者的读写权限。 sb.st_mode 包含了文件的权限位信息,通过与相应的宏定义进行“与”(&) 运算,就可以判断是否具有相应的权限。

2. chmod() 函数

chmod() 函数可以修改文件的权限。

#include <sys/stat.h>
#include <stdio.h>

int main() {
    // 将文件 "my_file.txt" 的权限设置为 "rw-r--r--" (644)
    if (chmod("my_file.txt", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
        perror("chmod");
        return 1;
    }

     // 更简洁的方式,直接使用八进制
    if (chmod("my_file.txt", 0644) == -1) {
          perror("chmod");
          return 1;
    }
    return 0;
}

这段代码用 chmod() 将 "my_file.txt" 的权限设置为 rw-r--r--。 可以用 | (或运算) 来组合多个权限位宏定义,也可以直接使用八进制数。

3. mkdir() 函数

创建目录的时候,也可以指定目录的初始权限。

#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>

int main()
{
     // 创建一个名为 "my_dir" 的目录,权限设置为 "rwxr-xr-x" (755)
	if(mkdir("my_dir", 0755) == -1)
	{
		perror("mkdir");
		return 1;
	}

	return 0;
}

这个示例程序创建目录, 指定初始权限。

五、 特殊权限位:SUID, SGID, Sticky Bit

除了上面说的读、写、执行权限,还有三个特殊的权限位:

  • SUID (Set User ID): 当一个设置了 SUID 位的可执行文件被执行时,进程的有效用户 ID 会变成该文件的所有者 ID,而不是执行该文件的用户 ID。 这通常用于需要 root 权限才能执行的任务,比如 passwd 命令。 SUID 的八进制值是 04000,宏定义为 S_ISUID
  • SGID (Set Group ID): 类似于 SUID,但影响的是进程的有效用户组 ID。 如果对目录设置了 SGID 位,那么在该目录中创建的新文件的用户组 ID 会继承该目录的用户组 ID,而不是创建文件的用户的默认用户组 ID。 SGID 的八进制值是 02000,宏定义为 S_ISGID
  • Sticky Bit: 最初用于将程序的文本镜像保存在交换区以提高运行速度。 如今对目录使用,具有粘滞位的目录, 里面的文件只有所有者才可以删除. 例如/tmp, 每个用户都可以在该目录创建文件, 但是不能删除其他用户的文件. 粘滞位的八进制是01000, 宏定义为 S_ISVTX.

ls -l 查看文件权限时,特殊权限位会显示在原来的执行权限位上:

  • SUID: 如果设置了 SUID 位,且文件所有者有执行权限,则显示 s;如果没有执行权限,则显示 S
  • SGID: 如果设置了 SGID 位,且用户组有执行权限,则显示 s;如果没有执行权限,则显示 S
  • Sticky Bit: 如果设置 Sticky Bit, 且其它用户具有执行权限, 则显示 t; 如果没有执行权限,则显示T.

六. 进阶应用和安全建议

  1. umask: umask (user mask) 是一个设置, 可以决定文件或目录在创建时,默认 具有哪些权限。 通过设置umask, 能够更细粒度地控制系统安全性. 举例: 你的 umask 值是 0022, 新建一个文件时, 这个文件默认权限是644 (666 - 022). umask 0022意味着新建文件对组和其他人不会自动获得写权限.

  2. 最小权限原则: 给文件或目录设置权限时, 应秉持"最小权限原则", 只给用户完成任务 必须 的权限, 多余的权限不要给。

  3. 谨慎使用 SUID/SGID: 尤其对脚本, 一定要确保来源安全可靠. 不安全的代码加上 SUID/SGID 可能导致严重安全漏洞.

  4. 利用ACL(访问控制列表): ACL 提供比基本权限更复杂的控制, 可以对特定用户/组设定权限。

总之,Linux 的权限位系统是理解和保障系统安全的关键。 通过熟练掌握权限位的表示方法和使用技巧, 能更好管理你的系统, 防止不必要的风险。