我们继续学习 Linux 系统编程中的重要函数。这次我们介绍 chmod 函数,它用于改变文件的访问权限。
data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">chmod 函数简介
1. 函数介绍
chmod 是一个 Linux 系统调用,用于改变文件或目录的访问权限(也称为文件模式位)。这些权限决定了哪些用户可以读取、写入或执行文件。
文件权限是 Unix/Linux 系统安全模型的基础。每个文件都有三组权限位:所有者(user)、所属组(group)和其他用户(others)。每组权限又包含三种基本权限:读(read, r)、写(write, w)和执行(execute, x)。
通过 chmod,具有适当权限的用户(通常是文件所有者或 root)可以调整这些权限,以控制对文件的访问。例如,一个用户可能希望保护一个私密文件,使其只能被自己读取;或者希望让一个脚本文件对所有用户都可执行。
2. 函数原型
1 | #include <sys/stat.h> // 必需 |
3. 功能
改变文件权限: 将由 pathname 指定的文件或目录的访问权限设置为 mode 参数指定的值。
设置绝对权限: mode 参数通常是一个八进制数(如 0644, 0755)或通过位运算组合的符号常量(如 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)。
4. 参数
- const char *pathname: 指向一个以空字符 (\0) 结尾的字符串,该字符串包含了要更改权限的文件或目录的路径名。这可以是相对路径或绝对路径。
mode_t mode: 指定新的文件权限。这个参数可以有两种表示方式:
八进制表示法:
最常见的形式,如 0644, 0755, 0600。
第一个数字 0 表示这是一个八进制数。
接下来的三位数字分别代表所有者(user)、组(group)、其他用户(others)的权限。
每一位的值是读(4)、写(2)、执行(1)的组合:
7 (4+2+1) = 读+写+执行 (rwx)
6 (4+2) = 读+写 (rw-)
5 (4+1) = 读+执行 (r-x)
4 (4) = 只读 (r–)
0 = 无权限 (—)
例如:
0644 表示所有者:读写 (6),组和其他用户:只读 (4)。常用于普通文件。
0755 表示所有者:读写执行 (7),组和其他用户:读执行 (5)。常用于可执行文件或目录。
0600 表示所有者:读写 (6),组和其他用户:无权限 (0)。常用于私密文件。
符号常量表示法:
- 使用 <sys/stat.h> 中定义的宏进行位运算组合。
用户类别:
S_IRWXU: 所有者的读、写、执行权限
S_IRUSR: 所有者的读权限
S_IWUSR: 所有者的写权限
S_IXUSR: 所有者的执行权限
组类别:
S_IRWXG: 组的读、写、执行权限
S_IRGRP: 组的读权限
S_IWGRP: 组的写权限
S_IXGRP: 组的执行权限
其他用户类别:
S_IRWXO: 其他用户的读、写、执行权限
S_IROTH: 其他用户的读权限
S_IWOTH: 其他用户的写权限
S_IXOTH: 其他用户的执行权限
特殊位:
S_ISUID: 设置用户ID位 (set-user-ID)
S_ISGID: 设置组ID位 (set-group-ID)
S_ISVTX: 粘滞位 (sticky bit)
例如:
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 等价于 0644。
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH 等价于 0755。
5. 返回值
- 成功时: 返回 0。
失败时:
返回 -1,并设置全局变量 errno 来指示具体的错误原因:
EACCES: 搜索路径名中的某个目录被拒绝。
EROFS: 路径名存在于只读文件系统上。
EIO: 执行 I/O 错误。
ELOOP: 解析 pathname 时遇到符号链接环。
ENAMETOOLONG: 路径名过长。
ENOENT: 文件不存在。
ENOMEM: 路径名无法分配内存。
ENOTDIR: 路径名前缀不是一个目录。
EPERM: 操作不被文件系统或操作系统允许。例如,尝试在某些文件系统上设置 set-group-ID 位。
EFAULT: pathname 指针指向进程地址空间之外。
EINVAL: mode 参数无效。
6. 相似函数,或关联函数
fchmod(int fd, mode_t mode): 与 chmod 功能相同,但通过已打开的文件描述符而不是路径名来指定文件。这可以避免路径解析。
fchmodat(int dirfd, const char *pathname, mode_t mode, int flags): 更现代的函数,允许使用相对路径(相对于 dirfd 描述符对应的目录)并提供额外的标志。
umask(mode_t mask): 设置进程的文件权限掩码,它会影响后续创建的文件的默认权限。
stat, lstat, fstat: 这些函数可以用来获取文件的当前权限,而不是设置它们。
7. 示例代码
示例 1:基本的权限更改
这个例子演示了如何使用 chmod 来更改文件的权限,包括八进制和符号常量两种方式。
1 | #include <sys/stat.h> // chmod, stat, struct stat |
代码解释:
定义了两个辅助函数:
print_permissions: 将 mode_t 类型的权限值转换为人类可读的字符串(如 -rw-r–r–)。
print_file_info: 使用 stat 获取并打印文件的详细信息,包括权限。
main 函数接受文件路径和权限字符串作为参数。
它使用 strtol 将权限字符串解析为八进制数。
调用 print_file_info 显示更改前的状态。
调用 chmod(pathname, new_mode) 执行权限更改。
如果成功,再次调用 print_file_info 显示更改后的状态。
示例 2:批量权限管理
这个例子模拟了一个简单的批量权限管理工具,可以同时更改多个文件的权限。
1 | #define _GNU_SOURCE |
代码解释:
change_permissions_recursive 函数实现了递归的权限更改功能,使用 opendir 和 readdir 遍历目录。
它使用 fnmatch 函数来支持通配符模式匹配(如 *.txt, *.log)。
print_mode_summary 函数以人类可读的方式显示权限设置。
main 函数处理命令行参数,支持递归(-r)、详细输出(-v)和文件名模式匹配(-p)选项。
程序会统计成功更改的文件数量和错误数量。
示例 3:权限安全和最佳实践
这个例子重点演示权限设置的安全考虑和最佳实践。
1 | #include <sys/stat.h> |
代码解释:
create_test_files 函数创建了几种不同类型的测试文件。
demonstrate_secure_permissions 演示了如何为不同类型的文件设置安全的权限。
demonstrate_dangerous_permissions 警告了一些常见的危险权限设置。
demonstrate_permission_checking 展示了如何检查现有文件的权限是否合理。
cleanup_test_files 负责清理创建的测试文件。
main 函数协调整个演示过程,并在最后总结权限设置的最佳实践。
编译和运行:
1 | # 编译示例 |
总结:
chmod 函数是 Linux 文件系统权限管理的核心工具。掌握其使用方法对于系统安全和文件访问控制至关重要。在使用时应遵循最小权限原则,根据文件的实际用途设置合适的权限,并定期检查重要文件的权限设置,以防止安全漏洞。