fchown – 修改文件所有者(通过文件描述符)
1. 函数介绍
fchown
是一个 Linux 系统调用,用于修改已打开文件的所有者(owner)和所属组(group)。与 chown
不同,fchown
通过文件描述符而不是文件路径来指定要修改的文件,这样可以避免在多线程环境中因文件重命名或删除而导致的竞态条件。
你可以把它想象成通过”门把手”而不是”地址”来找到房子并更换房主,这样即使房子的地址变了,你仍然可以通过门把手找到它。
2. 函数原型
#include <unistd.h>
int fchown(int fd, uid_t owner, gid_t group);
3. 功能
修改通过文件描述符指定的文件的所有者和所属组。如果只想修改所有者或所属组中的一个,可以将另一个参数设置为 -1。
4. 参数
int fd
: 文件描述符,通过open()
等函数获得uid_t owner
: 新的所有者用户 ID- 有效的用户 ID:设置为指定用户所有
(uid_t)-1
:保持当前所有者不变
gid_t group
: 新的所属组 ID- 有效的组 ID:设置为指定组所有
(gid_t)-1
:保持当前所属组不变
5. 返回值
- 成功时返回 0
- 失败时返回 -1,并设置
errno
6. 相似函数,或关联函数
chown()
: 通过文件路径修改文件所有者fchownat()
: 相对于目录文件描述符修改文件所有者lchown()
: 修改符号链接本身的所有者(而不是链接指向的文件)getuid()
: 获取当前用户 IDgetgid()
: 获取当前组 ID
7. 示例代码
示例1:基本使用 – 修改文件所有者
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main() {
int fd;
int ret;
// 创建测试文件
fd = open("test_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建文件失败");
exit(EXIT_FAILURE);
}
printf("成功创建文件,文件描述符: %d\n", fd);
// 写入一些内容
const char *content = "这是一个测试文件\n";
write(fd, content, strlen(content));
// 修改文件所有者为 root (uid=0),组为 root (gid=0)
// 注意:这需要适当的权限(通常是 root 权限)
ret = fchown(fd, 0, 0);
if (ret == -1) {
if (errno == EPERM) {
printf("权限不足:需要 root 权限才能将文件所有者改为 root\n");
} else {
perror("fchown 调用失败");
}
} else {
printf("成功将文件所有者修改为 root:root\n");
}
// 只修改所有者,保持组不变
ret = fchown(fd, getuid(), -1); // -1 表示不修改组
if (ret == -1) {
perror("修改所有者失败");
} else {
printf("成功将文件所有者修改为当前用户\n");
}
// 只修改组,保持所有者不变
ret = fchown(fd, -1, getgid()); // -1 表示不修改所有者
if (ret == -1) {
perror("修改组失败");
} else {
printf("成功将文件组修改为当前组\n");
}
close(fd);
printf("文件已关闭\n");
return 0;
}
示例2:检查权限并安全修改所有者
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main() {
int fd;
int ret;
struct stat file_info;
// 打开系统文件进行测试
fd = open("/etc/passwd", O_RDONLY);
if (fd == -1) {
perror("打开 /etc/passwd 失败");
exit(EXIT_FAILURE);
}
printf("成功打开文件,文件描述符: %d\n", fd);
// 获取文件当前信息
if (fstat(fd, &file_info) == -1) {
perror("获取文件信息失败");
close(fd);
exit(EXIT_FAILURE);
}
printf("文件当前信息:\n");
printf(" 所有者 UID: %d\n", file_info.st_uid);
printf(" 所属组 GID: %d\n", file_info.st_gid);
printf(" 文件大小: %ld 字节\n", file_info.st_size);
// 尝试修改所有者(这通常会失败,除非我们有适当权限)
ret = fchown(fd, 1000, 1000); // 尝试修改为 UID 1000, GID 1000
if (ret == -1) {
switch (errno) {
case EPERM:
printf("权限拒绝:修改文件所有者需要适当权限\n");
break;
case EBADF:
printf("无效的文件描述符\n");
break;
case EROFS:
printf("文件位于只读文件系统上\n");
break;
case EIO:
printf("I/O 错误\n");
break;
default:
printf("其他错误: %s\n", strerror(errno));
break;
}
} else {
printf("成功修改文件所有者\n");
}
close(fd);
printf("文件已关闭\n");
return 0;
}