好的,我们来深入学习 umask 系统调用,从 Linux 编程小白的角度出发。

1. 函数介绍 链接到标题

在 Linux 系统中,每个文件和目录都有与之相关的权限(Permissions),这些权限决定了谁(用户、组、其他人)可以对文件或目录执行什么操作(读、写、执行)。

当你创建一个新文件或目录时,系统需要决定这个新文件/目录的初始权限是什么。你可能会想当然地认为,比如用 touch newfile.txt 创建文件,这个文件的权限应该是 rw-rw-rw- (666),即所有用户都可读写。但实际上,Linux 为了安全,默认创建的文件权限会更严格一些。

umask (User File Creation Mask) 不是一个系统调用,而是一个 shell 内置命令 和一个 C 标准库函数。它用于设置或查询当前用户的文件权限掩码

这个“掩码”就像一个“过滤器”或“模具”。当你创建一个新文件或目录时,系统会先有一个“默认”的宽松权限(文件是 666,目录是 777),然后用 umask 的值来“过滤”掉(屏蔽掉)其中某些权限位,最终得到文件或目录的实际权限。

简单来说,umask 就是让你设置一个“权限过滤器”,决定了你新建的文件和目录默认有多“开放”。

典型应用场景

  • 用户自定义默认权限:用户可以通过 umask 命令设置自己的默认权限偏好。例如,设置 umask 077 可以让新建的文件和目录只有自己能访问,提高隐私性。
  • 程序设置安全默认值:编写程序时,可以在程序开始时调用 umask() 来设置一个安全的掩码,确保程序创建的临时文件等不会被其他用户意外访问。

2. 函数原型 链接到标题

#include <sys/stat.h> // 包含 umask 函数声明

// 设置文件权限掩码并返回旧的掩码
mode_t umask(mode_t mask);

3. 功能 链接到标题

设置调用进程(及其子进程)的文件权限创建掩码为 mask,并返回调用前的旧掩码。

4. 参数 链接到标题

  • mask:
    • mode_t 类型。
    • 指定新的文件权限掩码。这个值通常用八进制表示(以 0 开头,如 022, 077, 002)。
    • 掩码中的位为 1 表示对应的权限将被屏蔽(从默认权限中移除)。
    • 掩码中的位为 0 表示对应的权限不会被屏蔽(保留默认权限中的该位)。

5. 返回值 链接到标题

  • 总是返回调用 umask 之前的旧掩码。这使得程序可以临时改变掩码,用完后再恢复。

6. 权限计算方式 链接到标题

这是理解 umask 的关键:

  1. 确定默认权限
    • 新文件:默认权限是 666 (rw-rw-rw-)。
    • 新目录:默认权限是 777 (rwxrwxrwx)。
  2. 应用掩码:将默认权限与 umask 进行按位与非 (& ~) 操作。
    • 最终权限 = 默认权限 & (~ umask)

示例

  • umask 022:

    • 文件默认权限:666 (110 110 110)
    • umask:022 (000 010 010)
    • ~umask755 (111 101 101)
    • 文件最终权限:666 & 755 = 644 (rw-r–r–)
    • 目录默认权限:777 (111 111 111)
    • 目录最终权限:777 & 755 = 755 (rwxr-xr-x)
    • 效果:同组用户和其他用户失去了写权限。
  • umask 077:

    • 文件默认权限:666 (110 110 110)
    • umask:077 (000 111 111)
    • ~umask700 (111 000 000)
    • 文件最终权限:666 & 700 = 600 (rw——-)
    • 目录默认权限:777 (111 111 111)
    • 目录最终权限:777 & 700 = 700 (rwx——)
    • 效果:只有文件所有者有权限,同组和其他用户没有任何权限。这是非常私密的设置。
  • umask 002:

    • 文件默认权限:666 (110 110 110)
    • umask:002 (000 000 010)
    • ~umask775 (111 111 101)
    • 文件最终权限:666 & 775 = 664 (rw-rw-r–)
    • 目录默认权限:777 (111 111 111)
    • 目录最终权限:777 & 775 = 775 (rwxrwxr-x)
    • 效果:只有“其他人”失去了写权限,组内用户有完全权限。常用于协作环境。

7. 相似函数或关联函数 链接到标题

  • chmod: 用于更改已存在文件或目录的权限。
  • mkdir / open / creat: 这些创建文件或目录的函数会受到 umask 的影响。
  • umask shell 命令: 用于在 shell 中查看或设置当前 shell 会话的 umask 值。

8. 示例代码 链接到标题

下面的示例演示了如何在 C 程序中使用 umask() 函数来控制创建文件和目录的默认权限。

#define _GNU_SOURCE // 启用 GNU 扩展
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h> // 包含 umask, mkdir, stat
#include <fcntl.h>    // 包含 open, O_* flags
#include <string.h>
#include <errno.h>

// 辅助函数:将 mode_t 转换为字符串表示 (简化版)
void print_permissions(mode_t mode) {
    printf( (mode & S_IRUSR) ? "r" : "-");
    printf( (mode & S_IWUSR) ? "w" : "-");
    printf( (mode & S_IXUSR) ? "x" : "-");
    printf( (mode & S_IRGRP) ? "r" : "-");
    printf( (mode & S_IWGRP) ? "w" : "-");
    printf( (mode & S_IXGRP) ? "x" : "-");
    printf( (mode & S_IROTH) ? "r" : "-");
    printf( (mode & S_IWOTH) ? "w" : "-");
    printf( (mode & S_IXOTH) ? "x" : "-");
}

// 辅助函数:获取并打印文件/目录的权限
void check_permissions(const char* path) {
    struct stat sb;
    if (stat(path, &sb) == 0) {
        printf("Permissions for '%s': ", path);
        print_permissions(sb.st_mode);
        printf(" (%o)\n", sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
    } else {
        perror("stat");
    }
}

int main() {
    mode_t old_mask;
    int fd;

    printf("--- Demonstrating umask ---\n");
    printf("PID: %d\n", getpid());

    // 1. 查看当前的 umask 值
    // 通过设置 umask(0) 并立即恢复旧值来查询
    old_mask = umask(0);
    umask(old_mask); // 立即恢复
    printf("Initial umask: %03o\n", old_mask);

    // 2. 创建文件和目录,使用初始 umask
    printf("\n--- Creating files/dirs with initial umask ---\n");
    fd = open("file_with_initial_umask.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
    if (fd != -1) close(fd);
    mkdir("dir_with_initial_umask", 0777);
    check_permissions("file_with_initial_umask.txt");
    check_permissions("dir_with_initial_umask");

    // 3. 改变 umask 为 077 (非常私密)
    printf("\n--- Changing umask to 077 ---\n");
    old_mask = umask(0077); // 返回旧的 umask
    printf("Old umask was: %03o\n", old_mask);
    printf("New umask is: 077\n");

    // 4. 再次创建文件和目录
    fd = open("file_with_umask_077.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
    if (fd != -1) close(fd);
    mkdir("dir_with_umask_077", 0777);
    check_permissions("file_with_umask_077.txt");
    check_permissions("dir_with_umask_077");

    // 5. 改变 umask 为 002 (组协作)
    printf("\n--- Changing umask to 002 ---\n");
    old_mask = umask(0002);
    printf("Old umask was: %03o\n", old_mask);
    printf("New umask is: 002\n");

    // 6. 再次创建文件和目录
    fd = open("file_with_umask_002.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
    if (fd != -1) close(fd);
    mkdir("dir_with_umask_002", 0777);
    check_permissions("file_with_umask_002.txt");
    check_permissions("dir_with_umask_002");

    // 7. 恢复原始 umask
    printf("\n--- Restoring original umask ---\n");
    umask(old_mask);
    printf("Restored umask to: %03o\n", old_mask);

    printf("\n--- Summary ---\n");
    printf("1. umask acts as a filter on default permissions (666 for files, 777 for dirs).\n");
    printf("2. Bits set to 1 in umask REMOVE the corresponding permission.\n");
    printf("3. umask 022: Owner has full access, Group/Others have read/execute (not write).\n");
    printf("4. umask 077: Only Owner has access (very private).\n");
    printf("5. umask 002: Owner/Group have full access, Others lack write (collaborative).\n");
    printf("6. The umask set in a program affects files/dirs it creates.\n");

    // 8. 清理 (可选)
    // unlink("file_with_initial_umask.txt");
    // rmdir("dir_with_initial_umask");
    // unlink("file_with_umask_077.txt");
    // rmdir("dir_with_umask_077");
    // unlink("file_with_umask_002.txt");
    // rmdir("dir_with_umask_002");

    return 0;
}

9. 编译和运行 链接到标题

# 假设代码保存在 umask_example.c 中
gcc -o umask_example umask_example.c

# 运行程序
./umask_example

10. 预期输出 (取决于你运行时的初始 umask) 链接到标题

假设你的初始 umask022

--- Demonstrating umask ---
PID: 12345
Initial umask: 022

--- Creating files/dirs with initial umask ---
Permissions for 'file_with_initial_umask.txt': rw-r--r-- (644)
Permissions for 'dir_with_initial_umask': rwxr-xr-x (755)

--- Changing umask to 077 ---
Old umask was: 022
New umask is: 077
Permissions for 'file_with_umask_077.txt': rw------- (600)
Permissions for 'dir_with_umask_077': rwx------ (700)

--- Changing umask to 002 ---
Old umask was: 077
New umask is: 002
Permissions for 'file_with_umask_002.txt': rw-rw-r-- (664)
Permissions for 'dir_with_umask_002': rwxrwxr-x (775)

--- Restoring original umask ---
Restored umask to: 002

--- Summary ---
1. umask acts as a filter on default permissions (666 for files, 777 for dirs).
2. Bits set to 1 in umask REMOVE the corresponding permission.
3. umask 022: Owner has full access, Group/Others have read/execute (not write).
4. umask 077: Only Owner has access (very private).
5. umask 002: Owner/Group have full access, Others lack write (collaborative).
6. The umask set in a program affects files/dirs it creates.

11. 在 Shell 中使用 umask 命令 链接到标题

你也可以直接在终端中使用 umask 命令:

# 查看当前 umask
umask

# 设置 umask 为 077
umask 077

# 创建一个文件测试权限
touch test_file_umask_077.txt
ls -l test_file_umask_077.txt
# 输出类似:-rw------- 1 user user 0 date time test_file_umask_077.txt

# 恢复为常见的 022
umask 022
touch test_file_umask_022.txt
ls -l test_file_umask_022.txt
# 输出类似:-rw-r--r-- 1 user user 0 date time test_file_umask_022.txt

12. 总结 链接到标题

umask() 函数(以及 umask shell 命令)是 Linux 系统中控制新建文件和目录默认权限的重要工具。

  • 它通过一个“掩码”来过滤掉默认权限中的某些位。
  • 理解其计算方式(默认权限 & ~umask)是掌握它的关键。
  • 常见的 umask 值:
    • 022:最常见的默认值,保护文件不被同组和他人修改。
    • 077:最私密,只有所有者能访问。
    • 002:协作环境常用,保护文件不被他人修改,但同组用户有完全权限。
  • 在编写需要创建文件的程序时,合理设置 umask 可以增强程序的安全性。
  • 它只影响新创建的文件和目录,不影响已存在的文件。