66. 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;
}
67. fchownat - 相对于目录文件描述符修改文件所有者 見出しへのリンク
1. 函数介绍 見出しへのリンク
fchownat
是 fchown
的扩展版本,允许相对于指定目录文件描述符修改文件的所有者。它结合了 fchown
和 chownat
的特性,提供了更灵活的文件所有者修改方式。
这个函数特别适用于处理相对路径和在受限环境中操作文件,比如容器或沙箱环境。
2. 函数原型 見出しへのリンク
#include <fcntl.h>
#include <unistd.h>
int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags);
3. 功能 見出しへのリンク
修改相对于目录文件描述符的文件所有者和所属组。支持相对路径和额外的控制标志。
4. 参数 見出しへのリンク
int dirfd
: 目录文件描述符AT_FDCWD
: 使用当前工作目录- 有效的目录文件描述符:相对于该目录进行操作
const char *pathname
: 文件路径名(相对或绝对)uid_t owner
: 新的所有者用户 ID- 有效的用户 ID:设置为指定用户所有
(uid_t)-1
:保持当前所有者不变
gid_t group
: 新的所属组 ID- 有效的组 ID:设置为指定组所有
(gid_t)-1
:保持当前所属组不变
int flags
: 控制标志0
: 基本行为AT_SYMLINK_NOFOLLOW
: 不跟随符号链接(修改符号链接本身)
5. 返回值 見出しへのリンク
- 成功时返回 0
- 失败时返回 -1,并设置
errno
6. 相似函数,或关联函数 見出しへのリンク
fchown()
: 通过文件描述符修改文件所有者chown()
: 通过路径修改文件所有者lchown()
: 修改符号链接本身的所有者fchmodat()
: 相对于目录文件描述符修改文件权限openat()
: 相对于目录文件描述符打开文件
7. 示例代码 見出しへのリンク
示例1:基本使用 - 相对于目录修改文件所有者 見出しへのリンク
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main() {
int dirfd;
int ret;
// 创建测试目录
if (mkdir("test_dir", 0755) == -1 && errno != EEXIST) {
perror("创建测试目录失败");
exit(EXIT_FAILURE);
}
// 在测试目录中创建文件
int fd = open("test_dir/test_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
exit(EXIT_FAILURE);
}
const char *content = "测试文件内容\n";
write(fd, content, strlen(content));
close(fd);
// 打开目录获取文件描述符
dirfd = open("test_dir", O_RDONLY);
if (dirfd == -1) {
perror("打开目录失败");
exit(EXIT_FAILURE);
}
printf("成功打开目录,目录文件描述符: %d\n", dirfd);
// 相对于目录文件描述符修改文件所有者
// 注意:这需要适当的权限
ret = fchownat(dirfd, "test_file.txt", -1, -1, 0);
if (ret == -1) {
if (errno == EPERM) {
printf("权限不足:需要适当权限才能修改文件所有者\n");
} else if (errno == ENOTDIR) {
printf("dirfd 不是目录文件描述符\n");
} else {
perror("fchownat 调用失败");
}
} else {
printf("成功修改文件所有者\n");
}
// 使用 AT_FDCWD(当前工作目录)修改文件所有者
ret = fchownat(AT_FDCWD, "test_dir/test_file.txt", -1, -1, 0);
if (ret == -1) {
perror("使用 AT_FDCWD 修改所有者失败");
} else {
printf("使用 AT_FDCWD 成功修改文件所有者\n");
}
close(dirfd);
printf("目录已关闭\n");
return 0;
}
示例2:处理符号链接 見出しへのリンク
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main() {
int ret;
// 创建测试文件
int fd = open("target_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建目标文件失败");
exit(EXIT_FAILURE);
}
close(fd);
// 创建指向目标文件的符号链接
if (symlink("target_file.txt", "link_to_file") == -1) {
if (errno != EEXIST) {
perror("创建符号链接失败");
exit(EXIT_FAILURE);
}
}
printf("创建了符号链接 link_to_file -> target_file.txt\n");
// 默认行为:跟随符号链接,修改目标文件的所有者
ret = fchownat(AT_FDCWD, "link_to_file", -1, -1, 0);
if (ret == -1) {
perror("修改符号链接指向的文件失败");
} else {
printf("修改了符号链接指向的文件的所有者\n");
}
// 使用 AT_SYMLINK_NOFOLLOW:修改符号链接本身的所有者
ret = fchownat(AT_FDCWD, "link_to_file", -1, -1, AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
if (errno == EOPNOTSUPP) {
printf("当前文件系统不支持修改符号链接的所有者\n");
} else {
perror("修改符号链接本身失败");
}
} else {
printf("修改了符号链接本身的所有者\n");
}
// 清理测试文件
unlink("target_file.txt");
unlink("link_to_file");
return 0;
}
总结 見出しへのリンク
fchown
和 fchownat
是用于修改文件所有者和所属组的重要系统调用:
fchown
通过文件描述符操作,避免了路径相关的竞态条件fchownat
提供了更灵活的操作方式,支持相对路径和额外标志- 这些函数通常需要适当的权限(如 root 权限)才能修改文件所有者
- 正确处理返回值和错误情况对于编写健壮的程序非常重要
- 在容器和沙箱环境中,这些函数提供了更安全的文件操作方式