好的,我们来深入学习 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
的关键:
- 确定默认权限:
- 新文件:默认权限是
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()
函数来控制创建文件和目录的默认权限。
#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) 链接到标题
假设你的初始 umask
是 022
:
--- 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
可以增强程序的安全性。 - 它只影响新创建的文件和目录,不影响已存在的文件。