umask系统调用及示例

我们来深入学习 umask 系统调用

1. 函数介绍

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

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

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

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

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

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

典型应用场景:

  • 用户自定义默认权限:用户可以通过 umask 命令设置自己的默认权限偏好。例如,设置 umask 077 可以让新建的文件和目录只有自己能访问,提高隐私性。

  • 程序设置安全默认值:编写程序时,可以在程序开始时调用 umask() 来设置一个安全的掩码,确保程序创建的临时文件等不会被其他用户意外访问。

2. 函数原型

1
2
3
4
5
#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 的关键:

确定默认权限:

  • 新文件:默认权限是 666 (rw-rw-rw-)。

  • 新目录:默认权限是 777 (rwxrwxrwx)。

应用掩码:将默认权限与 umask 进行按位与非 (& ~) 操作。

  • 最终权限 = 默认权限 & (~ umask)

示例:

umask 022:

  • 文件默认权限:666 (110 110 110)

  • umask:022 (000 010 010)

  • ~umask:755 (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)

  • ~umask:700 (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)

  • ~umask:775 (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() 函数来控制创建文件和目录的默认权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#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. 编译和运行

1
2
3
4
5
6
# 假设代码保存在 umask_example.c 中
gcc -o umask_example umask_example.c

# 运行程序
./umask_example

10. 预期输出 (取决于你运行时的初始 umask)

假设你的初始 umask 是 022:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
--- 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 命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看当前 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 可以增强程序的安全性。

它只影响新创建的文件和目录,不影响已存在的文件。

data-ad-format="auto" data-full-width-responsive="true">