首页
/ SerenityOS 文件所有权修改指南:chown 命令的完整使用与底层实现解析

SerenityOS 文件所有权修改指南:chown 命令的完整使用与底层实现解析

2026-09-09 15:23:24作者:冯爽妲Honey

导读

chown 是 SerenityOS 中用于修改文件属主(owner)与属组(group)的核心命令。本指南以系统手册 chown(1) 为骨架,完整覆盖其语法、权限约束、全部命令行选项与实战示例,并结合仓库中的用户态实现 chown.cpp、内核系统调用 chown.cpp 与权限测试用例 TestKernelFilePermissions.cpp,带你从命令行操作一路深入到内核 sys$chown 的调用链,掌握 SerenityOS 所有权变更机制的完整图景。

命令用途与权限语义

chown 用于修改指定文件的属主(owner)与属组(group)。其核心语义如下:

  • 将文件属主修改为 user
  • 将文件属组修改为 group
  • 若省略 group,则文件的属组保持不变

值得特别强调的是 SerenityOS 对该命令的权限约束(手册 Description 中的 NOTE):

  • 只有超级用户(superuser)才能修改文件的属主(user ownership)
  • 普通用户可以使用 chown 将文件的属组修改为自己所属的其他组(即修改为其成员身份所在的组),这是普通用户唯一被允许的操作。

这一约束与内核侧的实现相呼应:内核在处理所有权变更时会基于进程的凭据(credentials)进行权限校验,普通进程无法任意将文件属主改给他人。

命令语法

chown 的命令行语法为:

$ chown <user[:group]> <path...>

其中:

  • <user[:group]> 是“所有权规格”(spec):由属主名(或属主 UID 数字)与可选的属组名(或属组 GID 数字)组成,二者以冒号 : 分隔;
  • <path...> 是一个或多个待修改的文件路径,chown 会依次处理所有传入的路径。

从用户态实现 chown.cpp 可以看到,命令行参数由 LibCore::ArgsParser 解析:USER[:GROUP] 是位置参数,PATH 是可变数量的位置参数,另有三个选项 -h--no-dereference)、-R--recursive)和 -L

所有权规格(USER[:GROUP])的解析规则

规格字符串的解析是理解 chown 行为的关键,源码 chown.cpp 展示了完整的处理逻辑:

1. 格式合法性校验

auto parts = spec.split_view(':', SplitBehavior::KeepEmpty);
if (parts[0].is_empty() || (parts.size() == 2 && parts[1].is_empty()) || parts.size() > 2) {
    warnln("Invalid uid/gid spec");
    return 1;
}
  • user 部分不能为空;
  • 若提供了 group,其内容不能为空(即 user: 这种写法是非法的);
  • 冒号最多出现一次(parts.size() > 2 报错)。

2. 属主(user)的解析:数字优先,名称其次

auto number = parts[0].to_number<uid_t>();
if (number.has_value()) {
    new_uid = number.value();
} else {
    auto passwd = TRY(Core::System::getpwnam(parts[0]));
    ...
    new_uid = passwd->pw_uid;
}
  • user 部分能解析为数字,则直接作为 UID 使用;
  • 否则通过 getpwnam() 按用户名查询密码数据库,取其 pw_uid 作为 UID;
  • 若用户名不存在,输出 Unknown user '...' 并返回退出码 1。

3. 属组(group)的解析(仅在提供 group 部分时进行)

  • 若能解析为数字,直接作为 GID;
  • 否则通过 getgrnam() 按组名查询组数据库,取其 gr_gid
  • 若组名不存在,输出 Unknown group '...' 并返回退出码 1。

例如 chown anon:anon file 中,anon 会被解析两次:一次作为用户名查 UID,一次作为组名查 GID。

命令行选项详解

手册中定义了三个选项,逐一说明如下:

-h, --no-dereference:不跟随符号链接

默认情况下,chown 会解引用符号链接,即修改链接目标文件的所有权;加上 -h 后则改为修改符号链接本身的所有权。

在实现中,该选项决定了底层调用的分派(chown.cpp):

auto maybe_error = no_dereference
    ? Core::System::lchown(path, new_uid, new_gid)
    : Core::System::chown(path, new_uid, new_gid);
  • 开启 -h 时调用 lchown(),对符号链接自身操作;
  • 未开启时调用 chown(),跟随链接修改目标文件。

同时,-h 模式下对符号链接本身还有一层保护:当遍历到符号链接且既未开启 -L 也未显式指定该路径时,会直接跳过(见下文选项交互)。

-R, --recursive:递归修改目录树

开启后,chown 会递归地处理目录下的所有条目。实现采用递归函数 update_path_ownerchown.cpp):

if (recursive && S_ISDIR(stat.st_mode)) {
    Core::DirIterator it(path, Core::DirIterator::Flags::SkipParentAndBaseDir);
    while (it.has_next())
        success &= update_path_owner(it.next_full_path());
}
  • 使用 lstat() 获取路径状态(不跟随符号链接);
  • 若当前路径是目录且开启了 -R,则通过 Core::DirIterator 遍历子条目(跳过 ...),对每个子条目递归调用自身;
  • 任一文件操作失败都会使整体退出码变为 1。

-L:递归遍历时跟随符号链接

-L 仅在 -R 递归场景下有意义:它指示 chown 在递归进入目录树时跟随符号链接。注意 -L 没有长选项名,在 ArgsParser 中对应的长选项指针为 nullptr

选项之间的交互:链接处理优先级

递归遍历时对符号链接的处理逻辑在 chown.cpp

if (S_ISLNK(stat.st_mode) && !follow_symlinks && !paths.contains_slow(path))
    return false;

这条判断的含义是:当遍历到一个符号链接时——

  • 若开启了 -Lfollow_symlinks 为真),则继续处理(跟随链接修改目标);
  • 若该符号链接是命令行显式指定的路径paths.contains_slow(path)),则无论如何都要处理(此时由 -h 决定是改链接本身还是改目标);
  • 否则(递归过程中遇到、未开 -L),直接跳过该符号链接,不做任何修改。

这一设计与 POSIX 常见实现的默认行为一致:递归时不擅自解引用递归过程中发现的符号链接,避免意外修改链接目标。此外,当 -h-L 同时出现时,链接本身的操作仍受 -h 控制(调用 lchown),而递归进入目录的遍历行为由 -L 控制。

实战示例

手册给出了两个典型示例,这里结合解析规则展开说明:

# 将 'file' 的属主和属组都改为 'anon':
$ chown anon:anon file

anon:anon 中第一个 anon 作为用户名解析出 UID,第二个 anon 作为组名解析出 GID,最终 file 的属主与属组均变为 anon

# 将 'file' 的属主改为 'root',属组保持不变:
# chown root file

注意此例只提供了 user 部分,省略了 group,因此仅修改属主,属组不变。该操作必须由超级用户执行(普通用户无权限变更属主)。

结合前面讲到的解析规则,还可以派生出一组实用变体:

# 使用数字 UID/GID,避免名称解析:
$ chown 1000:1000 file

# 仅修改属组为 'staff'(属主不变),可由属主或超级用户执行:
$ chown :staff file

需要说明的是:chown :staff 这种“仅改组”写法依赖解析器对空 user 部分的处理;从当前 实现 看,user 部分为空会被判定为 Invalid uid/gid spec,因此更稳妥的“仅改属组”方式是用 chgrp 命令,或显式写出当前属主名。若希望只修改属组,SerenityOS 提供了专用的 chgrp 命令。

从命令行到内核:完整调用链剖析

用户态入口与安全承诺

chown 启动后第一件事是声明自己的系统调用承诺(pledge,chown.cpp):

TRY(Core::System::pledge("stdio rpath chown"));

即该进程只允许使用标准 IO、读取路径(rpath)以及 chown 相关系统调用,这是 SerenityOS 的进程权限收窄机制,缩小了程序被利用后的攻击面。

LibCore 系统调用封装

Core::System 对内核接口做了两层封装(System.cpp):

  • chown(pathname, uid, gid):构造 Syscall::SC_chown_params,设置 follow_symlinks = true,发起 SC_chown 系统调用;
  • lchown(pathname, uid, gid):同样走 SC_chown,但 follow_symlinks = false
  • 另提供 fchown(fd, uid, gid),直接对已打开的文件描述符操作,对应内核的 sys$fchown

内核系统调用实现

内核侧实现在 Kernel/Syscalls/chown.cpp

  • sys$chown:首先 require_promise(Pledge::chown) 校验进程是否持有 chown pledge;随后从用户空间拷贝参数、解析路径,最终调用 VirtualFileSystem::chown() 执行实际的所有权变更。follow_symlinks 参数会转换为 O_NOFOLLOW_NOERROR 标志传给 VFS;
  • sys$fchown:通过文件描述符取得 OpenFileDescription,调用其 chown() 方法完成变更。

从源码结构看,VirtualFileSystem::chown 会依据 O_NOFOLLOW_NOERROR 决定是否解引用符号链接,并在此过程中基于进程凭据执行超级用户/组成员资格校验,这正是手册中权限约束(只有超级用户能改属主、普通用户只能改到自己的组)的底层依据。

测试验证

仓库中的内核测试 TestKernelFilePermissions.cpp 提供了一个 test_change_file_ownership 用例:通过 fchown(fd, getuid(), getgid()) 将临时文件的属主/属组改回调用者自身,随后断言 setuid/setgid 位被正确清除。它验证了 fchown 调用链与权限位的联动行为,可作为理解所有权变更机制的最小可运行示例。

退出码

  • 所有路径处理成功时返回 0
  • 参数规格非法、用户名/组名未知、或任一文件操作失败时返回 1chown.cpp)。

在脚本中应检查退出码以确认所有权变更是否完整生效。

相关命令

所有权与权限管理是一个命令族,与之配套的还有:

  • chgrp:仅修改文件的属组,支持数字 GID 或组名,提供 -h/--no-dereference 选项。当只需要改组时,它是比 chown 更精准的选择;
  • chmod:修改文件权限位(mode),支持八进制模式(如 7504711)与符号模式(如 o+rg=r),提供 -R/--recursive 递归选项。

三者配合使用,即可完整掌控 SerenityOS 中文件的属主(owner)、属组(group)与访问权限(mode)三个维度的管理。若需在脚本中自动化此类操作,可参考 TestKernelFilePermissions.cpp 中对 fchown/fchmod/fstat 等系统调用的组合用法。

热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.94 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
603
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
527