OpenHarmony北向开发基础之 沙箱机制+分布式文件

OpenHarmony 沙箱机制+分布式文件总结

适用范围:OpenHarmony 5.0.1标准系统(init 服务层 + appspawn 应用层)

参考代码://base/startup/init//base/startup/appspawn

路径约定:文中 //xxx/... 表示 OpenHarmony 源码根路径;/xxx/... 表示设备运行时路径)

关联案例:

  • 基础对照//foundation/filemanagement/dfs_service/services/distributedfile.cfgdistributedfiledaemonsandbox: 0(官方 dfs 服务,需访问动态挂载的 /mnt/hmdfs/

0. 各操作系统沙箱路径对比

说明 :本节用于建立直觉,便于从路径视角理解 OH 沙箱;各系统实现细节、版本差异较大,不作为安全审计或合规依据。OH 机制以 §2--§5 及源码为准。

沙箱并非 OpenHarmony 独有,但各系统的隔离粒度、路径约定、是否强制差异很大。

0.1 总览对比

操作系统 是否有沙箱 系统服务/守护进程 应用/第三方程序 典型沙箱数据路径 核心隔离手段
OpenHarmony ✅ 有(双层) init 服务沙箱,可配 sandbox: 0/1 HAP 默认进 appspawn 沙箱 服务:/mnt/sandbox/system 应用默认根:/mnt/sandbox/<userId>/<PackageName> 应用数据(进程内视角):/data/storage/el2/base/ mount namespace;应用层可选 pid/net namespace
Android ✅ 有(应用强制) 系统服务按 uid/SELinux 域隔离,无统一「沙箱路径」 第三方 APK 强制沙箱 /data/data/<包名>/ 多用户:/data/user/<uid>/<包名>/ Linux uid 隔离 + SELinux + mount namespace(Android 7+)
Linux(桌面/服务器) ⚠️ 可选,非默认 一般无沙箱,以 uid/gid + 文件权限为主 原生程序默认应用沙箱 用户目录:/home/<user>/ 容器:/var/lib/docker/... discretionary ACL;可选:AppArmor、SELinux、namespace、chroot
Windows ⚠️ 部分有 Windows 服务以 Service Account 运行,非路径沙箱 UWP/商店应用有沙箱;传统 Win32 默认无 UWP:%LOCALAPPDATA%\Packages\<包族名>\LocalState Win32:无固定沙箱路径 AppContainer、完整性级别(MIC);Win32 靠用户权限
macOS ✅ 有(应用级) launchd 系统服务,权限较宽 签名应用可启用 App Sandbox(商店/公证常见) ~/Library/Containers/<BundleID>/Data/ 临时:/private/var/folders/... sandbox-exec(Seatbelt)+ entitlement
iOS / iPadOS(苹果移动端) ✅ 有(最严格) 系统进程高度受限 所有 App 强制沙箱,无法关闭 容器:/var/mobile/Containers/Data/Application/<UUID>/ (开发者通过 API 访问,不直接暴露路径) 内核强制 + entitlement;无用户可选关闭

0.2 路径视角详细对照

对比维度 OpenHarmony Android Linux Windows macOS iOS
应用私有数据 /data/storage/el2/base/ /data/data/<包名>/ ~/$XDG_DATA_HOME UWP 包目录内 ~/Library/Containers/<BundleID>/ App 容器 Documents 等
应用安装目录 /system/app/<PackageName>/(只读映射) /data/app/...(系统管理) /usr/bin/opt/... Program Files\ /Applications/<App>.app 只读 App Bundle
沙箱根目录(假根) 服务:/mnt/sandbox/system;应用默认 /mnt/sandbox/<userId>/<PackageName>//base/startup/appspawn/modules/sandbox/sandbox_utils.cpp 无统一 pivot 假根,主要靠 uid 隔离数据目录 容器内 /(Docker 等) UWP 虚拟化文件系统 容器 Data 子树 整个 App 容器
系统服务数据 /data/service/el1/... /data/system/、各服务私有目录 /var/lib/.../etc/ C:\Windows\System32\... /Library/... 系统分区(App 不可达)
跨应用共享文件 remotefileshare / HMDFS ContentProvider、SAF 共享目录 + 权限 公共文档库 App Group 容器 App Group、DocumentPicker
能否关闭应用沙箱 仅系统集成可关(sandbox-switch: OFF 否(第三方 APK) N/A(默认无沙箱) UWP 不可关;Win32 本身无沙箱 开发者可选择是否启用(上架常强制)
系统服务能否不进沙箱 sandbox: 0(如 dfs、需直访 HMDFS 的业务 SA) 系统服务通常高权限运行 默认即全局视野 服务以 SYSTEM 等高权限账户运行 系统守护进程无 App Sandbox 系统守护进程无 App Sandbox

0.3 与 OpenHarmony 的关键差异

text 复制代码
                    OpenHarmony 的独特之处
                              │
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
  双层沙箱体系           服务可显式豁免          分布式文件路径复杂
  init(服务)+          cfg 中 sandbox:0/1      HAP:distributedfiles
  appspawn(应用)       不像 Android 应用        SA:/mnt/hmdfs/device_view
                         那样一律强制
差异点 说明
双层 vs 单层 OH 对系统 SAHAP 应用分别用 init / appspawn 两套沙箱;Android 主要约束应用层,系统服务靠 SELinux 域区分
服务可退出沙箱 OH 的 sandbox: 0 是显式配置项;Android/Linux 系统服务通常以高权限账户直接运行,概念上类似「不进沙箱」但无统一开关
路径假根 服务沙箱:PrepareSandbox 阶段 pivot_root 到 /mnt/sandbox/system;应用沙箱默认 /mnt/sandbox/<userId>/<PackageName>
与 Android 的相似性 均基于 Linux 内核;OH 应用层沙箱(mount namespace + uid + SELinux)设计思路与 Android 接近,但服务层多了 init sandbox
与苹果生态的相似性 iOS/macOS 应用沙箱同样强制、靠 entitlement 授权额外能力;OH 的 permission + module.json5 与之类似
Linux 桌面差异 传统 Linux 原生程序默认无沙箱,自由度最高、隔离最弱;需 Flatpak/Snap/容器等额外机制

0.4 读本文档时的定位

  • 若熟悉 Android :把 OH 应用沙箱类比为「强制 mount namespace + uid 隔离」;额外关注 init 服务层 sandbox: 0/1(Android 应用开发者通常接触不到这层)。
  • 若熟悉 iOS/macOS :把 OH permission 类比为 entitlement;context.filesDir 类比为 App 沙箱容器路径 API。
  • 若熟悉 Linux 桌面:OH 沙箱比传统 Linux 严格得多,更接近 Android/iOS 的移动安全模型,而非 Ubuntu 默认体验。
  • 若熟悉 Windows :OH 应用沙箱类似 UWP AppContainer;sandbox: 0 的系统服务类似以 SYSTEM/管理员权限运行的 Windows 服务。

1. 为什么需要沙箱

1.1 背景问题

OpenHarmony 设备上同时运行大量互不信任的代码:系统服务、第三方 HAP、厂商扩展组件。若所有进程共享同一套文件系统视图,一旦某个组件被攻破或存在漏洞,攻击者可以:

  • 读取其他应用/服务的私有数据(通讯录、密钥、业务缓存)
  • 篡改系统配置或关键二进制
  • 横向移动到其他进程(读写 /data、挂载点、设备节点)
  • 持久化驻留或提权

沙箱的核心诉求是:在操作系统层面,把「谁能看到什么」限制在最小必要范围内

1.2 沙箱最大的作用

沙箱的最大作用可以概括为一句话:

通过隔离进程的文件系统视图(及可选的 pid/net namespace),实现最小权限与故障/攻击 containment(把影响限制在边界内)。

具体体现在三个层面:

作用 说明 举例
隔离(Isolation) 每个进程只能看到自己沙箱内的路径 应用 A 无法直接读应用 B 的 /data/storage/el2/base/
最小权限(Least Privilege) 只挂载业务必需的白名单路径 普通 SA 只能看 /system/data 等预置目录
遏制扩散(Containment) 单点被攻破后,难以访问沙箱外资源 普通 HAP 通常无法直接对 HMDFS 执行底层 ioctl(还受权限/SELinux 约束)

沙箱不是 加密、不是 身份认证,也不能替代 SELinux / AccessToken / uid 隔离------它们与沙箱叠加,构成纵深防御。

text 复制代码
纵深防御栈(由外到内):
  应用权限(AccessToken / module.json5)
        ↓
  SELinux(secon 标签,**若产品启用**)
        ↓
  uid/gid + Linux capabilities
        ↓
  沙箱(mount namespace 文件视图隔离)  ← 本文重点
        ↓
  内核(HMDFS / devsl / ioctl 权限)

1.3 什么时候必须关心沙箱

场景 是否必须显式配置
新增系统 SA 服务 是(*.cfgsandbox
新增 HAP / ArkUI 应用 通常否(默认进应用沙箱)
访问 /mnt/hmdfs/ioctl share 是(服务层 sandbox: 0 + gid/permission)
插件 .so 随宿主加载 继承宿主,不单独配置
hdc shell 调试命令 (见下表 §1.3.1)
1.3.1 hdc shell 到底有没有沙箱?

直接回答:没有。 hdc shell 执行的命令既不进 init 服务沙箱,也不进 appspawn 应用沙箱

沙箱类型 hdc shell 是否进入 说明
init 服务沙箱 ❌ 不进 命令由 hdcd fork 出 /system/bin/sh 执行;hdcd 在 cfg 中 sandbox: 0//developtools/hdc/src/daemon/etc/hdcd.cfg),子进程继承其 全局 mount namespace
appspawn 应用沙箱 ❌ 不进 不经过 appspawn,不会进入 /mnt/sandbox/<userId>/<PackageName>/
uid / SELinux 限制 ✅ 仍有 cfg 中 hdcd 默认 uid: shell实测 调试产品(const.debuggable=1)上 hdc shellhdcd 进程均为 rootpersist.hdc.root 未设置时 hdc 默认保持 root)。无论 uid 为何,均不进 init/appspawn 沙箱
text 复制代码
hdc shell ls /mnt/hmdfs/
  │
  ├─ 不是 HAP → 无应用沙箱
  ├─ 不是 init sandbox:1 服务 → 无服务沙箱裁剪
  └─ 是 shell 用户 + 全局文件系统视图 → 所以常能直接看到 /mnt/hmdfs/
       (与 HAP 内 context.distributedFilesDir 视角完全不同)

「继承 shell 上下文」的旧说法 :指进程以 shell 用户身份、在 hdcd 的全局 mount namespace 中运行,不是指进了某种沙箱。旧表述易误解,已改为上表。

开发者误区 :shell 里能 ls 到的路径,不能推断 HAP 应用内也能访问。


2. 总览

OpenHarmony 的「沙箱」不是单一机制,而是两套独立体系,分别约束不同运行形态的进程:

维度 服务层沙箱(init) 应用层沙箱(appspawn)
约束对象 原生系统服务(SA / daemon) HAP 应用进程
配置入口 *.cfg"sandbox": 0/1(服务 cfg 位于子系统 etc///vendor/.../etc/init/ 源码 //base/startup/appspawn/appdata-sandbox.json,设备 /etc/sandbox/*.json
管理者 init 进程 appspawn / nwebspawn
隔离手段 mount namespace(服务:PrepareSandbox + EnterSandbox mount namespace + bind mount(应用)
典型路径 服务:/mnt/sandbox/system 应用:/mnt/sandbox/<userId>/<PackageName>
数据目录 /data/service/... /data/storage/el2/base/...
text 复制代码
init (PID 1)                    appspawn (sandbox: 0)
  │                               │
  ├── example_business_sa         ├── 拉起 HAP 进程
  │     sandbox: 0                │     → SandboxUtils::SetAppSandboxProperty()
  ├── distributedfiledaemon       │     → 进入 /mnt/sandbox/<userId>/<包名>/
  │     sandbox: 0                │
  └── 某系统服务                   └── 应用只能看白名单路径
        sandbox: 1
        → EnterSandbox("system")

核心原则 :沙箱是隔离 (限制每个进程能看什么),不是共享工作区(大家进同一个沙箱互访)。


3. 服务层沙箱(init Service Sandbox)

3.1 机制说明

服务层沙箱由 init 实施,通过 Linux mount namespace 隔离文件系统视图。完整流程分两段(均在 //base/startup/init/services/sandbox/sandbox.c):

  1. 启动阶段PrepareSandbox):在 /mnt/sandbox/<name>/ 建沙箱根,按 JSON 白名单 bind mount,并 pivot_root 到该根目录
  2. 服务拉起阶段SetServiceEnterSandboxEnterSandbox):子进程 fork 后、exec 前,切换进已建好的 mount namespace

子进程不会再次 执行 pivot_root;它进入的是启动阶段已准备好的沙箱视图。

init 进程自身不在沙箱内sandbox 配置只作用于 init 拉起的子服务

3.2 在沙箱内 vs 不在沙箱内

可以把「在沙箱内 / 不在沙箱内」理解成:进程看到的文件系统是不是被「裁剪过」

一句话区别
状态 本质
不在沙箱内 进程与系统共用同一套 mount namespace/mnt/hmdfs/ 等后续挂载都能实时看到
在沙箱内 进程拥有独立的 mount namespace ,根目录被切到 /mnt/sandbox/system,只能看到白名单内的路径
进程视角对比
text 复制代码
【不在沙箱内】example_business_sa (sandbox: 0)
  进程视角的 "/"  =  系统真实根目录
  /mnt/hmdfs/...  ← dfs 后来 mount 的,能立刻看到

【在沙箱内】某普通 SA (sandbox: 1)
  进程视角的 "/"  =  /mnt/sandbox/system/(假根)
  /system、/data  ← 白名单 bind mount 进来的
  /mnt            ← 启动时快照,private 传播
  /mnt/hmdfs/...  ← dfs 后来 mount 的,看不到 ❌
init 拉起子服务时做了什么

init 在 fork 之后、exec 之前调用 SetServiceEnterSandbox()//base/startup/init/services/init/standard/init_service.c):

c 复制代码
// sandbox: 0 → 直接 return,不做任何事
if (service->attribute & SERVICE_ATTR_WITHOUT_SANDBOX) {
    return 0;   // 子进程继续用 init 的 mount namespace
}

// sandbox: 1 → 子进程切换进已准备好的沙箱 mount namespace
EnterSandbox("system");   // SetNamespace(CLONE_NEWNS),见 sandbox.c
  → 沙箱视图在启动阶段 PrepareSandbox 中已 pivot_root 建好
  → 白名单来自 //base/startup/init/services/sandbox/system-sandbox64.json
  → 子进程 "/" 对应沙箱内的文件系统视图

init 自身从不调用 EnterSandbox(),原因:

  1. init 是 PID 1,负责 mount 全局文件系统、创建 /mnt/sandbox/、拉起所有服务
  2. 若 init 也进沙箱,会被限制在白名单内,无法管理后续动态挂载
  3. init 是沙箱的管理者,不是被管理者
四个维度对比
维度 不在沙箱内(sandbox: 0 在沙箱内(sandbox: 1
mount namespace 与 init / 宿主机共享 独立 namespace
根目录 / 系统真实根 /mnt/sandbox/system(假根)
可见路径 全局文件系统(仍受 uid/selinux 约束) //base/startup/init/services/sandbox/system-sandbox64.json 白名单
动态挂载 /mnt/hmdfs/ 等后续 mount 可见 private bind 时不可见(启动快照)
安全性 较低(视野大) 较高(视野小,最小权限)
典型服务 distributedfiledaemon、需访问 HMDFS 的业务 SA 可进沙箱的 /system/bin/ SA(多数需动态挂载的系统服务实际仍设 0
需访问 HMDFS 的 SA 实例

某业务 SA 设 sandbox: 0(不在沙箱内)的典型表现:

bash 复制代码
# 进程内能直接看到 dfs 动态挂载的路径
ls /mnt/hmdfs/100/account/device_view/local/services/<service_name>/.share/
ioctl(fd, HMDFS_IOC_SET_SHARE_PATH, ...)  # 能操作

若误设 sandbox: 1(在沙箱内):

bash 复制代码
# 沙箱内 /mnt 是启动时 private bind 的快照
ls /mnt/hmdfs/   # 目录不存在或为空
ioctl(...)       # ENOENT,share 注册失败
与 uid / SELinux 的区别

「不在沙箱内」不等于「无限制」:

text 复制代码
example_business_sa (sandbox: 0)
  ├── mount namespace:全局可见(不进沙箱)
  ├── uid/gid:业务账户 / dfs_share(身份限制)
  ├── SELinux:u:r:<service>_domain:s0(标签限制)
  └── permission:DISTRIBUTED_DATASYNC(令牌限制)

沙箱管的是文件系统视野 ;uid、SELinux、permission 管的是在这个视野里能做什么sandbox: 0 只是视野不被裁剪,其余安全机制仍然生效。

类比
角色 类比
init 大楼管理员,有全部钥匙,能开所有门、装新设备
sandbox: 1 的服务 租户,只能进自己房间和白名单公共区域
sandbox: 0 的服务 物业维修工,能进大楼所有区域,但仍需工牌(uid/selinux)

init 必须是管理员,否则无法给各服务建沙箱、做全局 mount;普通服务默认当租户,需要更大权限时才设 sandbox: 0

3.3 生效条件(两层开关)

服务是否真正进入沙箱,需同时满足

层级 条件 说明
系统开关 const.sandbox=enable 读自系统参数(init_service.cSystemReadParam("const.sandbox"));未 enable 时 sandbox: 1 不生效
服务开关 cfg 中 "sandbox": 1(非 0) sandbox: 0 会设置 SERVICE_ATTR_WITHOUT_SANDBOX 并直接跳过
可执行路径 path[0]/system/bin//vendor/bin/ 开头 否则 SetServiceEnterSandbox 不会 调用 EnterSandbox(见下方源码)

省略 sandbox 字段时GetServiceSandbox 不设置 WITHOUT_SANDBOX 标志(服务 calloc 初始为 0),在 const.sandbox=enable 且路径匹配时会尝试进沙箱 。建议显式写 01

源码逻辑(//base/startup/init/services/init/standard/init_service.c):

c 复制代码
// sandbox: 0 → MarkServiceWithoutSandbox → 直接返回,不进沙箱
if ((service->attribute & SERVICE_ATTR_WITHOUT_SANDBOX) == SERVICE_ATTR_WITHOUT_SANDBOX) {
    return 0;
}
// const.sandbox 未 enable → 也不进沙箱
if (g_enableSandbox == false) {
    return 0;
}
// 按可执行文件路径选择沙箱类型
if (strncmp(execPath, "/system/bin/", ...) == 0) {
    EnterSandbox("system");    // system-sandbox64.json
} else if (strncmp(execPath, "/vendor/bin/", ...) == 0) {
    EnterSandbox("chipset");   // chipset-sandbox64.json
}
// 路径不在上述前缀 → 返回 0,实际不进沙箱(即使 cfg 写了 sandbox:1)

3.4 配置方式

在服务的 *.cfg 文件中设置(示例为需访问 HMDFS 的业务 SA):

json 复制代码
{
  "services": [{
    "name": "example_business_service",
    "path": ["/system/bin/sa_main", "/system/profile/example_business_service.json"],
    "uid": "business",
    "gid": ["business", "shell", "dfs_share"],
    "secon": "u:r:example_business_service:s0",
    "sandbox": 0,
    "permission": ["ohos.permission.DISTRIBUTED_DATASYNC", "..."]
  }]
}
sandbox 含义 典型场景
**0** 设置 SERVICE_ATTR_WITHOUT_SANDBOX,不进 init 沙箱 需访问动态挂载路径的系统服务(HMDFS、存储等)
**1**(或非 0) 清除 WITHOUT_SANDBOX;在 const.sandbox=enable 且路径为 /system/bin//vendor/bin/ 时进沙箱 仅需白名单内路径的 SA
省略 等同未标记 WITHOUT_SANDBOX;满足条件时会进沙箱 务必显式写 01,避免歧义

注意 :只有 0 和「非 0」两种有效语义,没有 23 等级 。非 0 一般写 1

3.5 沙箱策略文件

系统预置沙箱白名单,源码位于 //base/startup/init/services/sandbox/,打包后位于设备 /etc/sandbox/

源码文件 设备路径 沙箱根 适用服务
//base/startup/init/services/sandbox/system-sandbox64.json /etc/sandbox/system-sandbox.json /mnt/sandbox/system /system/bin/ 下的服务
//base/startup/init/services/sandbox/chipset-sandbox64.json /etc/sandbox/chipset-sandbox.json /mnt/sandbox/chipset /vendor/bin/ 下的服务

白名单结构示例(//base/startup/init/services/sandbox/system-sandbox64.json 节选):

json 复制代码
{
  "sandbox-root": "/mnt/sandbox/system",
  "mount-bind-paths": [{
    "src-path": "/system/bin",
    "sandbox-path": "/system/bin",
    "sandbox-flags": ["bind", "rec", "private"]
  }, {
    "src-path": "/data",
    "sandbox-path": "/data",
    "sandbox-flags": ["bind", "rec", "private"]
  }, {
    "src-path": "/mnt",
    "sandbox-path": "/mnt",
    "sandbox-flags": ["bind", "rec", "private"]
  }]
}
字段 含义
sandbox-root 沙箱进程的「假根」目录
src-path 宿主机真实路径
sandbox-path 沙箱内映射路径
sandbox-flags 挂载传播属性;private 表示后续新挂载不传播进沙箱

3.6 为什么很多系统服务设 sandbox: 0

嵌入式 OH 产品上,大量系统服务使用 sandbox: 0,例如:

服务 源码 cfg sandbox 原因
distributedfiledaemon //foundation/filemanagement/dfs_service/services/distributedfile.cfg 0 需访问 /mnt/hmdfs/ 动态挂载、负责 HMDFS 挂载
hilogd //base/hiviewdfx/hilog/services/hilogd/etc/hilogd.cfg 0 需写 /log、读全局设备节点
storage_daemon //foundation/filemanagement/storage_service/services/storage_daemon/storage_daemon.cfg 0 管理存储挂载
需直访 HMDFS 的业务 SA 对应服务的 *.cfg(置于产品子系统或 //vendor/.../etc/init/ 0 ioctl share、读 device_view 路径

典型踩坑 :HMDFS 在 dfs_service 启动后才 mount 到 /mnt/hmdfs/。若服务 sandbox: 1/mntprivate 绑定,沙箱内看到的是启动时刻的快照,看不到后续动态挂载。

3.7 服务层其他安全约束(与 sandbox 并行)

sandbox: 0 不等于无安全限制,服务仍受以下约束:

机制 配置项 作用
用户/组 uidgid 进程身份
SELinux secon 强制访问控制标签
能力 caps Linux capabilities
权限 permission OH 访问令牌(AccessToken)
APL apl 应用权限等级(system_core / system_basic 等)

4. 应用层沙箱(appspawn App Sandbox)

4.1 机制说明

HAP 应用由 appspawn 进程孵化,启动时自动构建应用沙箱:

  1. 为每个应用创建沙箱目录,默认根路径 /mnt/sandbox/<userId>/<PackageName>userId = uid / 200000,见 sandbox_utils.cpp
  2. //base/startup/appspawn/appdata-sandbox.json 白名单 bind mount 所需路径
  3. 按配置创建 namespace:sandbox-ns-flags 可含 netpid默认 app-base 仅配置 net,见源码 JSON)
  4. 应用数据目录在进程内映射为 /data/storage/el2/base//data/storage/el2/distributedfiles/

appspawn 自身在 cfg 中也是 sandbox: 0(需管理所有应用沙箱):

json 复制代码
// 源码://base/startup/appspawn/appspawn.cfg
{
  "name": "appspawn",
  "path": ["/system/bin/appspawn", "-mode appspawn", "--sandbox-switch on", "..."],
  "sandbox": 0
}

4.2 沙箱策略文件

源码文件 设备路径 用途
//base/startup/appspawn/appdata-sandbox.json /etc/sandbox/appdata-sandbox.json 普通应用沙箱主配置
//base/startup/appspawn/appdata-sandbox-isolated.json /etc/sandbox/appdata-sandbox-isolated.json 隔离模式应用
vendor overlay(如 //vendor/<产品名>/appspawn/appdata-sandbox.json 覆盖设备 /etc/sandbox/ 按包名覆盖挂载规则

//base/startup/appspawn/appdata-sandbox.json 结构(节选):

json 复制代码
// 节选://base/startup/appspawn/appdata-sandbox.json(common.app-base)
{
  "common": [{
    "top-sandbox-switch": "ON",
    "app-base": [{
      "sandbox-ns-flags": ["net"],
      "mount-paths": [{
        "src-path": "/system/app",
        "sandbox-path": "/system/app",
        "sandbox-flags": ["bind", "rec"]
      }]
    }]
  }]
}

sandbox-root 未在 app-base 中配置时,运行时默认 /mnt/sandbox/<userId>/<PackageName>//base/startup/appspawn/modules/sandbox/sandbox_utils.cppGetSandboxRootPath)。

字段 含义
top-sandbox-switch 全局沙箱总开关:ON / OFF
sandbox-root 可选;未配置时默认 /mnt/sandbox/<userId>/<PackageName>;配置 <PackageName> 占位符时运行时替换
sandbox-ns-flags 额外 namespace:pid(进程隔离)、net(网络隔离)
mount-paths 应用可见路径白名单
sandbox-switch 单个应用/场景级开关(可覆盖)

4.3 应用层如何配置是否进沙箱

应用层没有 类似服务 cfg 的 sandbox: 0/1 单字段,而是通过多层配置组合:

配置层 源码位置 作用
全局开关 //base/startup/appspawn/appdata-sandbox.jsontop-sandbox-switch ON:默认所有应用进沙箱;OFF:全局关闭
应用级开关 同文件或 vendor overlay 中 sandbox-switch 按包名/场景单独 ON/OFF
appspawn 启动参数 //base/startup/appspawn/appspawn.cfg--sandbox-switch on/off appspawn 守护进程默认策略
包类型 HAP 包 module.json5 属性(normal / system / privileged 等) 影响沙箱严格程度和挂载范围
隔离模式 //base/startup/appspawn/appdata-sandbox-isolated.json 更严格的隔离策略

应用开发者通常不直接改 sandbox 开关,而是通过:

  • module.json5 声明权限与 requestPermissions
  • 使用应用沙箱内合法路径(如 context.filesDir
  • 分布式文件通过 remotefileshare / distributedfiles 路径访问(注意@ohos.remotefileshare 为产品集成 NAPI,标准公共 SDK 无此模块,见 §4.6.4

4.4 应用沙箱内的路径视角

应用看到的路径 实际对应
/data/storage/el2/base/ 应用私有数据目录
/data/storage/el2/distributedfiles/ 分布式文件目录
/data/storage/el2/distributedfiles/.share/ HAP 侧 share 注册路径(对比 SA 的 ioctl 方式)
/system/app/<PackageName>/ 系统应用安装目录(只读映射;三方应用路径因安装方式而异)

应用无法直接访问(未经沙箱映射的系统级路径):

  • /mnt/hmdfs/.../device_view/{networkId}/services/...(系统 HMDFS 真实消费路径)
  • /data/service/(系统服务数据)
  • 其他应用的数据目录

注:应用沙箱会将 /mnt/hmdfs/<userId>distributedfiles 做有限 bind mount(见 §5.5),但不能 替代 SA 层的 device_view + ioctl 能力。

4.5 应用层其他安全约束

机制 说明
AccessToken module.json5 声明的 requestPermissions
UID 隔离 每个应用独立 uid
SELinux 应用进程有独立 secon 标签
sandbox_manager 跨应用文件访问策略(persist policy)
沙箱路径校验 应用只能读写自己沙箱内的路径

4.6 案例:分布式音视频 HAP 与分布式文件

本节以典型的分布式音视频流转 HAP 为例,说明应用沙箱内如何使用分布式文件;不涉及具体产品包名与源码路径。

4.6.1 核心结论:应用不需要关闭沙箱

此类 HAP 没有 任何 sandbox-switch: OFF 或 vendor overlay 豁免配置。它默认留在应用沙箱内 ,通过系统预置的 distributedfiles 路径映射 + 框架 API 使用分布式文件。

text 复制代码
HAP 使用分布式文件 ≠ 关闭沙箱
HAP 使用分布式文件 = 在应用沙箱内,走合法路径 + remotefileshare API

与需直访 device_view 的业务 SA(sandbox: 0 + ioctl)是两条不同路线,见 §5.6、§7。

4.6.2 系统侧(应用开发者通常不改)

应用能访问分布式文件,依赖系统 //base/startup/appspawn/appdata-sandbox.json已预置的 bind mount(应用进沙箱时自动生效):

应用进程内路径(代码中使用) 宿主机 HMDFS 映射
/data/storage/el2/distributedfiles /mnt/hmdfs/<userId>/account/merge_view/data/<PackageName>
/data/storage/el2/distributedfiles/.share/ 同上目录下的 .share 子目录(share 注册落点)
/mnt/hmdfs/<userId> 同路径有限 bind mount

应用开发者不需要无法 在 HAP 包里修改上述系统沙箱白名单;若产品缺映射,需系统集成工程师改 appdata-sandbox.json overlay。
板端实测 (userId=100):com.ohos.launcher 的 mountinfo 显示

/data/storage/el2/distributedfiles ← bind ← /mnt/hmdfs/100/account/merge_view/data/com.ohos.launcher(见 §9.2.3)。

4.6.3 应用侧必须配置(典型做法)

① 声明权限module.json5requestPermissions):

json 复制代码
{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.DISTRIBUTED_DATASYNC" },
      { "name": "ohos.permission.ACCESS_SERVICE_DM" },
      { "name": "ohos.permission.MANAGE_DISTRIBUTED_ACCOUNTS" },
      { "name": "ohos.permission.INTERNET" },
      { "name": "ohos.permission.GET_NETWORK_INFO" },
      { "name": "ohos.permission.READ_MEDIA" }
    ]
  }
}

② 运行时申请关键权限(Stage 模型推荐):

typescript 复制代码
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';

const atManager = abilityAccessCtrl.createAtManager();
await atManager.requestPermissionsFromUser(context, ['ohos.permission.DISTRIBUTED_DATASYNC']);

FA 模型可使用 context.requestPermissionsFromUser(标注 @FAModelOnly);Stage 模型请用 abilityAccessCtrl

③ 只使用沙箱内合法路径 (禁止硬编码 /mnt/hmdfs/.../device_view/):

用途 典型路径常量
本地媒体资源 /data/storage/el1/bundle/context.filesDir 下路径
分布式 share 目录 /data/storage/el2/distributedfiles/.share
对端流转播放路径 want.parameters.filePath(如 .../distributedfiles/.share/xxx.mp4

④ 通过框架 API 注册 share不是 应用内 ioctl):

typescript 复制代码
import remotefileshare from '@ohos.remotefileshare'

// fileId 为本地文件 fd(先 open 沙箱内路径获得)
let sharePath = await remotefileshare.createSharePath(fileId, targetDeviceId)
// 返回:/data/storage/el2/distributedfiles/.share/...

⑤ 创建分布式目录 & 设置安全标签(Stage 模型):

typescript 复制代码
// Stage:直接读 context.distributedFilesDir(映射到 distributedfiles)
const distDir = context.distributedFilesDir;
const fullpath = distDir + '/.share/' + fileName;
securityLabel.setSecurityLabel(fullpath, 's0');

FA 模型可使用 context.getOrCreateDistributedDir()(同为 @FAModelOnly);路径语义与 Stage 的 distributedFilesDir 一致。

4.6.4 SDK 与系统库依赖(标准 OH vs 产品集成)

HAP 包内通常无自研 native 库 :此类应用多为纯 ArkTS/ETS;分布式文件能力来自系统镜像 中的 native 模块,运行时由 requireNapi 加载 /system/lib/module/libremotefileshare.z.so(模块名以产品集成为准)。

依赖分层(典型 import 结构):

层级 模块类型 来源 用途
应用封装 业务 ArkTS 封装类 应用自写 封装设备列表、share、远程拉起
文件共享 @ohos.remotefileshare 产品集成的系统 NAPI (如 libremotefileshare.z.so createSharePath(fd, networkId)
设备发现 @ohos.distributedDeviceManager 标准公共 SDK 信任设备列表、networkId
远程拉起 厂商扩展 NAPI(如 @ohos.*RemoteAppAbility 厂商/产品扩展 远程拉起对端应用并传递 filePath、续播进度
流转开关 厂商扩展 NAPI(如 @ohos.distributedSchedule.*) 厂商/产品扩展 控制是否允许被远程流转拉起
辅助 securityLabeldistributedDatafile.fs 标准公共 SDK 安全标签、元数据同步、本地文件 fd

标准 OH 公共 SDK 与产品侧实现的差异

组件 标准 OH(//foundation/filemanagement/app_file_service 产品集成(厂商按需补充)
C++ 能力 RemoteFileShare::CreateSharePath(innerkit,供框架/SA) 可对 innerkit 封装或独立实现(底层多为 ioctl HMDFS_IOC_SET_SHARE_PATH
ArkTS NAPI (仅 C++ innerkit + 单测,无 interfaces/kits/js 需编译 NAPI 模块(如 libremotefileshare.z.so)+ 对应 .d.ts
SDK 类型声明 @ohos.remotefileshare 需在 SDK API 声明目录补充类型定义(见下节)
完整音视频流转 无等价的远程应用调度 API 通常依赖厂商扩展的远程拉起 / 流转控制 NAPI

仅用标准 OH 公共 SDK(不集成产品侧 NAPI)时,HAP 侧 share-path 路线基本不可行

  • 公共 SDK 没有 @ohos.remotefileshare 模块与 d.ts;HAP 无法通过 ArkTS 调用 createSharePath
  • 标准 OH 提供的是 C++ innerkit RemoteFileShare//foundation/filemanagement/app_file_service/interfaces/innerkits/native/remote_file_share/)及 dfs_service 另一套分布式文件机制(P2P 会话、拷贝等),不面向 HAP 暴露与上述相同的 share-path 注册 API。
  • 若产品未将 NAPI 编进镜像并部署到 /system/lib/module/,应用编译期可能仅有类型占位,运行期 requireNapi 会失败。

厂商新建 NAPI 的集成要点 (以 remotefileshare 类模块为例,具体命名由产品定义):

步骤 做什么 通常放在哪
1. 实现 NAPI 桥接 将 innerkit 的 CreateSharePath 等能力导出为 ArkTS 可调用接口 //foundation/filemanagement/app_file_service/interfaces/kits/js/ 下新建子目录,或纳入厂商文件管理子系统
2. 编译进镜像 生成 lib<模块名>.z.so 对应子系统 BUILD.gn 增加 NAPI target,随产品镜像打包
3. 部署路径 运行时 requireNapi 加载 设备 /system/lib/module/lib<模块名>.z.so
4. SDK 类型声明 供应用编译期类型检查 //interface/sdk-js/api/@ohos.<模块名>.d.ts(或产品 SDK 声明目录)
5. 应用权限 调用方 HAP 声明分布式相关权限 module.json5requestPermissions
6. 沙箱映射 确保 distributedfiles 已映射 //base/startup/appspawn/appdata-sandbox.json 或 vendor overlay

当前系统库能否支撑 HAP 分布式文件?

场景 是否可行 条件
HAP 文件共享(createSharePath ⚠️ 视产品镜像 需镜像集成 NAPI(如 libremotefileshare.z.so);实测本产品线镜像未集成该模块
完整音视频流转(远程拉起 + 续播) ✅(含厂商扩展的产品) 另需远程应用调度、流转控制等 vendor NAPI
仅标准 OH 公共 SDK、零产品补丁 ❌(HAP share-path 路线) 无 ArkTS API;需改走 SA/innerkit 或其他分布式机制
业务 SA(sandbox: 0 直链 innerkit 或 ioctl,见 §4.6.7、§7
text 复制代码
HAP(应用沙箱内)
  ArkTS: remotefileshare.createSharePath(fd, cid)
           ↓ requireNapi
  系统: libremotefileshare.z.so(产品集成,非 HAP 自带)
           ↓ ioctl HMDFS_IOC_SET_SHARE_PATH
  内核: HMDFS → 沙箱映射路径 distributedfiles/.share/
4.6.5 端到端流程(与沙箱的关系)
text 复制代码
发布端(主设备 HAP,在应用沙箱内)
  1. open(沙箱内媒体路径) → 获得 fd
  2. remotefileshare.createSharePath(fd, targetDeviceId)
       → 框架在沙箱映射路径下创建 .share 条目
       → 应用看到:/data/storage/el2/distributedfiles/.share/xxx.mp4
  3. 远程应用调度 API 把 sharePath 传给对端

消费端(从设备 HAP,仍在应用沙箱内)
  1. onCreate 收到 want.parameters.filePath
       → /data/storage/el2/distributedfiles/.share/xxx.mp4
  2. 播放器直接播放沙箱内映射路径
4.6.6 开发者检查清单
检查项 推荐做法 错误做法
是否关闭应用沙箱 ❌ 不关闭,默认 top-sandbox-switch: ON 在 overlay 设 sandbox-switch: OFF
文件路径 /data/storage/el2/distributedfiles/.share/ /mnt/hmdfs/.../device_view/...
Share 注册 remotefileshare.createSharePath ioctl(HMDFS_IOC_SET_SHARE_PATH)
权限 DISTRIBUTED_DATASYNC + ACCESS_SERVICE_DM 仅声明不申请
调试对比 shell 能访问不代表 HAP 能访问 hdc shell 结果推断应用行为(见 §1.3.1)
系统 NAPI ✅ 镜像含对应 lib*.z.so 假定公共 SDK 自带 @ohos.remotefileshare
流转拉起 ✅ 使用产品提供的远程调度 NAPI 仅用标准 SDK 复现完整流转链路
4.6.7 与业务 SA 方案对比(见 §7)
维度 HAP(应用沙箱) 业务 SA(sandbox: 0
沙箱 默认进应用沙箱 不进 init 沙箱
Share remotefileshare.createSharePath(产品 NAPI) ioctl SET_SHARE_PATH(innerkit / 直调)
NAPI / SDK 依赖镜像 NAPI + 可选厂商扩展 API 不依赖 HAP NAPI,C++ innerkit 即可
路径 distributedfiles/.share/ /mnt/hmdfs/.../device_view/.../.share/
应用需改沙箱? N/A(非 HAP)

5. 各运行形态如何配置沙箱

在 OpenHarmony 中,不同形态的代码如何决定「是否进沙箱、进哪种沙箱」

5.1 总览对照表

运行形态 是否有独立进程 沙箱体系 配置入口 默认是否进沙箱
SA 系统服务 init 服务层 服务 *.cfg(如 //vendor/.../etc/init/ 或子系统 etc/)→ "sandbox": 0/1 视 cfg 与 const.sandbox
普通 SO 库 否(被 dlopen) 继承宿主 无独立配置 随宿主进程
命令行应用/工具 视拉起方式 init 或 shell 见 §5.3 视拉起方式
ArkUI / HAP 应用 appspawn 应用层 //base/startup/appspawn/appdata-sandbox.json 默认进
分布式文件场景 跨服务+应用 双层叠加 服务 cfg + 应用沙箱 + HMDFS 策略 最复杂,见 §5.6
text 复制代码
                    ┌─────────────────────────────────────┐
                    │         是否独立进程?               │
                    └──────────────┬──────────────────────┘
                           否     │      是
                    ┌────────────┴────────────┐
                    ▼                         ▼
              SO 库(继承宿主)          谁拉起的?
                    │              ┌───────┴────────┐
                    │              ▼                ▼
                    │         init (.cfg)      appspawn (HAP)
                    │         sandbox:0/1      top-sandbox-switch
                    │              │                │
                    └──────────────┴────────────────┘
                              最终 mount namespace

5.2 SA 系统服务

SA(System Ability)服务由 init 通过 sa_main 或独立二进制拉起,是服务层沙箱的直接配置对象。

配置步骤:

  1. 在服务的 *.cfg 中设置 "sandbox": 01
  2. 确认系统参数 const.sandbox=enable(仅 sandbox: 1 时需要)
  3. sandbox: 1,确认 //base/startup/init/services/sandbox/system-sandbox64.json 白名单覆盖所有依赖路径
  4. 并行配置 uid/gid/secon/permission/caps

示例(需访问 HMDFS 的业务 SA,不进沙箱):

json 复制代码
{
  "name": "example_business_service",
  "path": ["/system/bin/sa_main", "/system/profile/example_business_service.json"],
  "uid": "business",
  "gid": ["business", "shell", "dfs_share"],
  "secon": "u:r:example_business_service:s0",
  "sandbox": 0,
  "permission": ["ohos.permission.DISTRIBUTED_DATASYNC"]
}

示例(普通 SA,进沙箱):

json 复制代码
{
  "name": "some_system_service",
  "path": ["/system/bin/sa_main", "/system/profile/some_service.json"],
  "uid": "someuser",
  "gid": ["someuser"],
  "secon": "u:r:some_service:s0",
  "sandbox": 1
}
决策 sandbox: 0 sandbox: 1
需访问 /mnt/hmdfs/ 动态挂载 ❌(通常看不到后续挂载)
ioctl / setxattr 操作 HMDFS
仅需 /system/data 静态路径 可选 ✅ 推荐
安全审计要求严格隔离 ---

系统总开关:

bash 复制代码
# 查看
param get const.sandbox
# 产品集成时通常在 build/产品配置中设定,运行时只读

5.3 普通 SO 库(动态库)

.so 动态库不是独立进程,没有自己的沙箱配置项。

要点 说明
加载方式 宿主进程通过 dlopen / 链接器加载
沙箱归属 完全继承宿主进程的 mount namespace
能否单独设 sandbox 不能
代码权限 与宿主共享 uid/gid/SELinux 上下文

典型场景(SA 加载业务插件 .so):

text 复制代码
example_business_service (SA, sandbox: 0)
  └── dlopen("lib<plugin>.z.so")  // 插件随 SA 同进程加载
        └── 插件代码与 SA 同进程、同沙箱、同 uid/gid

若宿主 SA 为 sandbox: 0,插件 .so 可直接访问 /mnt/hmdfs/;若宿主为 sandbox: 1,插件同样受 init 沙箱限制。

集成建议:

  • 需要底层文件系统能力的逻辑,放在 SA 进程 + sandbox: 0 ,而非 HAP 内嵌 native .so
  • 插件 .so 的安全边界 = 宿主 SA 的安全边界,需在 SA 的 cfg 中一并规划

5.4 普通命令行应用

命令行程序的沙箱取决于如何被拉起 ,没有统一的 sandbox 字段。

拉起方式 沙箱行为 配置方法
init 注册为 service 与服务层 SA 相同 对应 *.cfg"sandbox": 0/1
**hdc shell 手动执行** 两层沙箱都不进 ;以 shell 用户 + 全局 mount namespace 运行(同 hdcdsandbox: 0 无沙箱开关;受 uid/gid、SELinux、文件权限约束(见 §1.3.1)
init job 一次性命令 在 init 上下文中执行,通常无沙箱 jobs.cmds 中定义
nativespawn 拉起的 native 应用 走 appspawn 应用沙箱 //base/startup/appspawn/nativespawn.cfg + //base/startup/appspawn/appdata-sandbox.json

init 注册的命令行服务示例 (来源 //developtools/hdc/src/daemon/etc/hdcd.cfg,节选):

json 复制代码
{
  "name": "hdcd",
  "path": ["/system/bin/hdcd"],
  "uid": "shell",
  "gid": ["shell", "log", "readproc", "file_manager"],
  "sandbox": 0
}

hdcd 作为调试守护进程需访问全局文件系统,故 sandbox: 0;cfg 中 uidshell (非 root)。部分产品 cfg 含 "disabled": 1,由产品决定是否启用 hdcd 服务。

**hdc shell 调试注意(详见 §1.3.1):**

bash 复制代码
hdc shell id
# 调试产品(const.debuggable=1)常见 uid=0(root);非调试产品多为 shell(2000)
# 无论 uid 为何,均不进 HAP/SA 沙箱

hdc shell ls /mnt/hmdfs/100/
# 若 dfs 已挂载且权限允许,shell 通常可见;不代表 HAP 内也能访问

开发者常见误区:在 shell 里能访问的路径,不代表 HAP 应用也能访问。

nativespawn(原生应用孵化,来源 //base/startup/appspawn/nativespawn.cfg):

json 复制代码
// 源码://base/startup/appspawn/nativespawn.cfg
{
  "name": "nativespawn",
  "path": ["/system/bin/nativespawn", "--sandbox-switch on", "..."],
  "sandbox": 0
}
  • nativespawn 自身 sandbox: 0(需管理子进程沙箱)
  • 它拉起的 native 应用子进程,由 --sandbox-switch on 决定是否进应用沙箱

5.5 ArkUI / HAP 应用

ArkUI 应用(Stage 模型 HAP)由 appspawn 孵化,默认进入应用沙箱 ,开发者通常无需、也无法在 HAP 包里写 sandbox: 0

配置层级(由粗到细):

层级 配置文件 / 参数 开关字段 作用
系统预置 //base/startup/appspawn/appdata-sandbox.json(设备 /etc/sandbox/appdata-sandbox.json top-sandbox-switch: ON/OFF 全局默认
appspawn 启动 //base/startup/appspawn/appspawn.cfg --sandbox-switch on/off 孵化守护进程策略
应用级覆盖 vendor overlay / 包名规则 sandbox-switch: ON/OFF 单包豁免或加强
隔离模式 //base/startup/appspawn/appdata-sandbox-isolated.json 独立策略集 更严格隔离
应用权限 HAP 内 module.json5 requestPermissions 沙箱内 API 访问控制

开发者侧配置(module.json5):

json5 复制代码
{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.DISTRIBUTED_DATASYNC" }
    ]
  }
}

文件路径使用规范:

typescript 复制代码
// ✅ 正确:使用 Context API 获取沙箱内合法路径
const filesDir = context.filesDir;
const distDir = context.distributedFilesDir;

// ❌ 错误:硬编码系统路径
const path = "/mnt/hmdfs/100/account/device_view/...";

应用沙箱内 HMDFS 的映射关系//base/startup/appspawn/appdata-sandbox.json):

应用看到的路径 实际宿主机映射
/data/storage/el2/distributedfiles /mnt/hmdfs/<userId>/account/merge_view/data/<PackageName>
/mnt/hmdfs/<userId> 同路径 bind mount(有限可见性)
/data/storage/el2/base 应用私有数据目录

应用不能 直接访问 device_view/{networkId}/services/.../.share/ 这类系统级 HMDFS 路径,需通过 remotefileshare 等框架 API。

如何「关闭」某个应用沙箱(仅系统集成,非应用开发者):

  1. 在 vendor overlay 的 appdata-sandbox.json(如 //vendor/<产品名>/appspawn/appdata-sandbox.json)中为该包名设 sandbox-switch: OFF
  2. 或全局 top-sandbox-switch: OFF极不推荐,破坏安全模型)

5.6 最复杂场景:分布式文件(HMDFS)

分布式文件是 OpenHarmony 沙箱体系中最复杂的场景,因为它同时涉及:

  • 服务层//foundation/filemanagement/dfs_service/、需直访 HMDFS 的业务 SA)--- init 沙箱
  • 应用层(分布式音视频等 HAP)--- appspawn 沙箱
  • 内核层//kernel/linux/.../fs/hmdfs/ share_table、devsl、ioctl)--- 与沙箱无关但强相关
  • 动态挂载/mnt/hmdfs/ 运行时 mount)--- 与 private bind 传播冲突
5.6.1 三层机制与沙箱的关系
text 复制代码
L1 Share 注册(ioctl SET_SHARE_PATH)
   ├── HAP 路径:remotefileshare.createSharePath(框架代劳)
   └── SA  路径:原生 ioctl(需 sandbox:0 + dfs_share gid)
        ↓
L2 P2P 建链(`//foundation/filemanagement/dfs_service/` → `DistributedFileDaemonManager::OpenP2PConnection`)
   └── distributedfiledaemon(`//foundation/filemanagement/dfs_service/services/distributedfile.cfg`,sandbox:0)负责
        ↓
L3 对端访问(open/read device_view/.share/)
   ├── HAP:/data/storage/el2/distributedfiles/.share/(沙箱映射路径)
   └── SA:/mnt/hmdfs/.../device_view/.../.share/(系统真实路径)
5.6.2 HAP vs SA 沙箱配置对比
维度 HAP(应用沙箱) SA(服务沙箱)
沙箱开关 top-sandbox-switch(默认 ON) sandbox: 0/1
Share 注册 API remotefileshare.createSharePath ioctl(HMDFS_IOC_SET_SHARE_PATH)
发布路径 .../distributedfiles/.share/ .../merge_view/services/{svc}/.share/
消费路径 框架映射的 distributedfiles device_view/{nid}/services/.../.share/
能否直接 ioctl 是(需 sandbox:0)
P2P 触发 框架自动 / 远程拉起 显式 OpenP2PConnection
关键 permission DISTRIBUTED_DATASYNC 同左 + cfg 中声明
关键 gid 应用 uid dfs_share
5.6.3 分布式文件相关服务的沙箱配置清单
组件 源码 cfg / 配置 sandbox 原因
distributedfiledaemon //foundation/filemanagement/dfs_service/services/distributedfile.cfg 0 负责 HMDFS mount、P2P
cloudfileservice //foundation/filemanagement/dfs_service/services/distributedfile.cfg 0 云同步访问全局路径
需直访 HMDFS 的业务 SA 对应服务 *.cfg(产品子系统或 //vendor/.../etc/init/ 0 ioctl share + 读 device_view
appspawn //base/startup/appspawn/appspawn.cfg 0 管理应用沙箱
nativespawn //base/startup/appspawn/nativespawn.cfg 0 管理 native 应用沙箱
普通 HAP HAP 内 module.json5 默认进应用沙箱 通过框架 API 间接访问 HMDFS
5.6.4 业务 SA 完整沙箱相关配置示例
json 复制代码
{
  "name": "example_business_service",
  "uid": "business",
  "gid": ["business", "shell", "dfs_share"],
  "secon": "u:r:example_business_service:s0",
  "sandbox": 0,
  "permission": [
    "ohos.permission.DISTRIBUTED_DATASYNC",
    "ohos.permission.DISTRIBUTED_SOFTBUS_CENTER"
  ]
}
配置项 作用
sandbox: 0 不进 init 沙箱,能同步看到 dfs 动态挂载的 /mnt/hmdfs/
gid: dfs_share 允许参与 HMDFS share 相关操作
secon: <service>_domain SELinux;share 后需 setxattr user.security=s0
DISTRIBUTED_DATASYNC OpenP2PConnection 权限校验
5.6.5 分布式文件沙箱踩坑汇总
现象 根因 解决
SA 内 /mnt/hmdfs/ 不存在 sandbox:1 + private mount 看不到动态挂载 sandbox: 0
HAP 内硬编码 /mnt/hmdfs/ 失败 应用在沙箱内,路径未映射或不可写 context.distributedFilesDir + 框架 API
share 注册 ENOENT merge_view .share 目录未创建 等 dfs 初始化或手动 mkdir
对端 device_view 不可见 仅 L1 share,未 L2 P2P 调用 OpenP2PConnection
shell 能访问但 HAP 不能 shell 不进两层沙箱,HAP 在应用沙箱内 按应用沙箱路径开发,勿以 shell 结果为准
插件 .so ioctl 失败 宿主 SA 为 sandbox:1 在 SA cfg 设 sandbox:0
5.6.6 选型建议(需直访 device_view 的场景)

当业务需要直接操作 device_view 路径、执行 ioctl(HMDFS_IOC_SET_SHARE_PATH) 时,宜选择 SA + sandbox:0 + 原生 innerkit/ioctl,而非 HAP 方案,原因:

  1. 需要直接操作 device_view 路径,HAP 沙箱内不可达
  2. HAP 侧应使用 remotefileshare 等框架 API,无法满足 SA 直访需求
  3. distributedfiledaemon(同为 sandbox:0)共享 mount namespace,保证动态挂载可见
  4. 业务插件 .so 若随 SA 加载,继承同一沙箱策略

6. 服务层 vs 应用层对比

对比项 服务层(init) 应用层(appspawn)
进程类型 原生 SA / daemon HAP 应用
源码配置 服务 *.cfg(子系统 etc///vendor/.../etc/init/ //base/startup/appspawn/appdata-sandbox.json
设备配置 /system/etc/init/xxx_service.cfg /etc/sandbox/appdata-sandbox.json
开关字段 "sandbox": 0/1 top-sandbox-switch / sandbox-switch
系统总开关 const.sandbox=enable appspawn --sandbox-switch//base/startup/appspawn/appspawn.cfg
沙箱根目录 /mnt/sandbox/system /mnt/sandbox/<userId>/<PackageName>(默认)
namespace 主要是 mount mount + 可配 pid/net(默认配置含 net
数据目录 /data/service/el1/... /data/storage/el2/base/...
能否访问 /mnt/hmdfs/ sandbox: 0 时可访问全局挂载 可映射 /mnt/hmdfs/<userId>distributedfiles不能 直接访问 device_view 系统路径
典型配置者 系统/平台集成工程师 系统预置 + 应用权限声明

7. HAP vs SA 选型(分布式文件场景)

7.1 何时用 HAP、何时用 SA

需跨设备共享文件时,常见两条路线:

维度 HAP(应用沙箱) 业务 SA(sandbox: 0
运行形态 应用沙箱进程 系统 SA 进程
Share 注册 ArkTS remotefileshare.createSharePath 原生 ioctl(HMDFS_IOC_SET_SHARE_PATH) 或 innerkit
HMDFS 路径 /data/storage/el2/distributedfiles/.share/ /mnt/hmdfs/.../device_view/.../.share/
配置 应用权限 + 沙箱内合法路径 服务 *.cfgsandbox: 0dfs_share gid、分布式 permission
适用场景 面向用户的应用、媒体流转、仅需框架映射路径 需直访 device_view、底层 ioctl、与 dfs 同 namespace 的后台业务

7.2 业务 SA 关键配置要点

json 复制代码
{
  "name": "example_business_service",
  "uid": "business",
  "gid": ["business", "shell", "dfs_share"],
  "secon": "u:r:example_business_service:s0",
  "sandbox": 0,
  "permission": [
    "ohos.permission.DISTRIBUTED_DATASYNC",
    "ohos.permission.DISTRIBUTED_SOFTBUS_CENTER"
  ]
}
配置项 与沙箱/安全的关系
sandbox: 0 不进 init 沙箱,能同步看到 dfs 挂载的 /mnt/hmdfs/
gid: dfs_share HMDFS share 操作的组权限
secon: <service>_domain SELinux 标签,share 时需 setxattr user.security=s0
permission: DISTRIBUTED_DATASYNC OpenP2PConnection 权限校验

7.3 若误设 sandbox: 1 的可能后果

const.sandbox=enable 的产品上:

  1. 进程进入 /mnt/sandbox/system,文件系统视图被隔离
  2. /mntprivate 绑定,后续 /mnt/hmdfs/ 挂载不可见
  3. ioctl SET_SHARE_PATH 目标路径不存在或不可写
  4. 读取 device_view/.share/ 失败

8. 配置决策指南

8.1 服务层:何时设 sandbox: 0

  • 需访问运行时动态挂载的路径(HMDFS、外置存储、网络文件系统)
  • 需执行 ioctl / setxattr 等底层文件系统操作
  • 需与多个系统服务共享同一 mount namespace(如 storage、dfs)
  • 参考同类系统服务(distributedfiledaemonstorage_daemon 均为 0

8.2 服务层:何时设 sandbox: 1

  • 服务仅需访问 /system/data 等静态白名单路径
  • 安全审计要求更严格的文件系统隔离
  • 确认 const.sandbox=enable 且白名单已覆盖所有依赖路径

8.3 应用层:开发者注意事项

  • 应用默认进沙箱top-sandbox-switch: ON),无需也不能像 cfg 那样写 sandbox: 0
  • 文件操作限制在 context.filesDircontext.distributedFilesDir 等 API 返回路径内
  • 跨设备文件共享使用框架 API(remotefileshare),不要硬编码 /mnt/hmdfs/
  • 需要额外路径时通过系统 overlay 配置或申请特殊权限,而非关闭沙箱

9. 调试与验证

9.1 实测结论速览

验证项 文档预期 本机实测 结论
const.sandbox enable 时服务沙箱生效 enable ✅ 一致
distributedfiledaemon sandbox:0 dfs 需不进沙箱 cfg 与进程 PID 3646 均确认 ✅ 一致
业务 SA sandbox:0 可见 hmdfs mountinfo 含 hmdfs thingmodel_service PID 13470 可见 ✅ 一致
/etc/sandbox/*.json 设备有沙箱策略 4 个文件均存在 ✅ 一致
top-sandbox-switch 默认 ON appdata-sandbox.jsonON ✅ 一致
应用沙箱根 /mnt/sandbox/<userId>/<PackageName> /mnt/sandbox/100/com.ohos.launcher/ 存在 ✅ 一致(userId=100)
HAP distributedfiles 映射 bind 到 merge_view launcher mountinfo 证实(见 §9.3) ✅ 一致
hdc shell 不进沙箱 全局 namespace shell 85 行 mount vs launcher 94 行,且映射不同 ✅ 一致
libremotefileshare.z.so 产品可选集成 不存在 ⚠️ 本镜像未集成 NAPI
device_view/.../.share/ share 后才有 无活跃 share 时目录不存在 ✅ 正常

9.2 验证命令与实测输出

9.2.1 系统沙箱开关
bash 复制代码
$HDC shell "param get const.sandbox"
# 实测:enable

$HDC shell "param get const.debuggable"
# 实测:1(调试产品;影响 hdc 默认 uid,见 §1.3.1)
9.2.2 服务层:dfs 与业务 SA
bash 复制代码
$HDC shell "ps -ef | grep distributedfiledaemon | grep -v grep"
# 实测:dfs 3646 ... distributedfiledaemon

$HDC shell "grep sandbox /system/etc/init/distributedfile.cfg"
# 实测:distributedfiledaemon / cloudfileservice 均为 "sandbox": 0

$HDC shell "cat /proc/3646/mountinfo | grep hmdfs | head -2"
# 实测(节选):
# ... /mnt/hmdfs/100/account ... hmdfs ...

$HDC shell "grep sandbox /system/etc/init/thingmodel_service.cfg"
# 实测:"sandbox": 0

$HDC shell "cat /proc/13470/mountinfo | grep hmdfs | head -1"
# 实测:thingmodel_service 同样可见 /mnt/hmdfs/100/account
9.2.3 应用层:沙箱目录与 HAP 映射
bash 复制代码
$HDC shell "ls /etc/sandbox/"
# 实测:appdata-sandbox.json、appdata-sandbox-isolated.json、
#       chipset-sandbox.json、system-sandbox.json

$HDC shell "ls /mnt/sandbox/"
# 实测:0、100、chipset、com.ohos.render、system

$HDC shell "ls /mnt/sandbox/100/ | head -5"
# 实测:com.ohos.launcher、com.ohos.settings、...

# launcher PID=2562,验证 distributedfiles 映射
$HDC shell "cat /proc/2562/mountinfo | grep distributedfiles"
# 实测(节选):
# ... /merge_view/data/com.ohos.launcher /data/storage/el2/distributedfiles ... hmdfs ...
9.2.4 分布式文件与 NAPI
bash 复制代码
$HDC shell "ls /mnt/hmdfs/"
# 实测:100

$HDC shell "ls /mnt/hmdfs/100/account/"
# 实测:cloud_merge_view、device_view、merge_view

$HDC shell "ls -l /system/lib/module/libremotefileshare.z.so"
# 实测:No such file or directory(本产品线镜像未集成)

$HDC shell "find /system -name '*remotefile*' 2>/dev/null"
# 实测:无结果
9.2.5 hdc shell 身份(不进沙箱)
bash 复制代码
$HDC shell "id"
# 实测:uid=0(root) ... context=u:r:su:s0
# 说明:const.debuggable=1 且 persist.hdc.root 未设置时,hdc 默认 root

$HDC shell "param get persist.hdc.root"
# 实测:Get parameter fail errNum:106(未设置)

$HDC shell "cat /proc/1253/status | grep Uid"
# 实测:hdcd 进程 Uid: 0(运行态亦为 root,与 cfg 中 uid:shell 可能因调试策略不同)
9.2.6 应用沙箱调试
bash 复制代码
$HDC shell "begetctl sandbox 2>&1 | head -1"
# 实测:sandbox -s | -n [-p] | -p | -b | -h

$HDC shell "begetctl sandbox -b 2562"
# 进入 com.ohos.launcher 的应用沙箱 shell(需 root)

9.3 shell 与 HAP 视角差异(实测对照)

视角 进程 mount 条目数(实测) /data/storage/el2/distributedfiles /mnt/hmdfs/100/account/device_view/...
hdc shell sh(root) 85 无此映射 shell 可直接 ls 部分 hmdfs 路径
HAP(launcher) com.ohos.launcher 94 ✅ bind 到 merge_view ❌ 无 device_view 直访映射

开发者误区(实测印证) :shell 能 ls /mnt/hmdfs/100/...,不代表 HAP 内能访问同一路径;HAP 应使用 context.distributedFilesDir 及框架 API。

9.4 常见问题

现象 可能原因 实测相关
/mnt/hmdfs/ 不存在 dfs 未启动 本机 dfs 正常,存在 /mnt/hmdfs/100
libremotefileshare.z.so 不存在 产品未集成 NAPI 本机确认未集成;HAP share-path 路线需产品补模块
device_view/.../.share/ 不存在 无活跃 share 注册 正常;share 注册后才会出现
sandbox:1 但行为像 0 const.sandbox 未 enable 本机为 enable,此项不适用
shell 能访问但 HAP 不能 shell 不进沙箱 launcher mountinfo 已证实映射差异

10. 参考

资源 源码路径
init 沙箱实现 //base/startup/init/services/sandbox/sandbox.c
服务 sandbox 解析 //base/startup/init/services/init/init_service_manager.cGetServiceSandbox()
服务 sandbox 生效 //base/startup/init/services/init/standard/init_service.cSetServiceEnterSandbox()
system 沙箱白名单 //base/startup/init/services/sandbox/system-sandbox64.json(设备 /etc/sandbox/system-sandbox.json
chipset 沙箱白名单 //base/startup/init/services/sandbox/chipset-sandbox64.json(设备 /etc/sandbox/chipset-sandbox.json
appspawn 沙箱入口 //base/startup/appspawn/modules/sandbox/sandbox_utils.cppSetAppSandboxProperty
begetctl 沙箱调试 //base/startup/init/services/begetctl/sandbox.cpp
appspawn 守护进程 cfg //base/startup/appspawn/appspawn.cfg
nativespawn cfg //base/startup/appspawn/nativespawn.cfg
应用沙箱配置(源码) //base/startup/appspawn/appdata-sandbox.json(设备 /etc/sandbox/appdata-sandbox.json
应用隔离沙箱配置 //base/startup/appspawn/appdata-sandbox-isolated.json
dfs_service 配置 //foundation/filemanagement/dfs_service/services/distributedfile.cfg
storage_daemon 配置 //foundation/filemanagement/storage_service/services/storage_daemon/storage_daemon.cfg
hdc 调试守护进程 cfg //developtools/hdc/src/daemon/etc/hdcd.cfg
HMDFS 内核 share 定义 //kernel/linux/.../fs/hmdfs/hmdfs_share.h(具体内核树路径因产品而异)
RemoteFileShare innerkit //foundation/filemanagement/app_file_service/interfaces/innerkits/native/remote_file_share/
remotefileshare NAPI(产品集成) 编译产物部署至设备 /system/lib/module/libremotefileshare.z.so;源码通常置于 app_file_service/interfaces/kits/js/ 或厂商文件管理子系统

11. 审校说明

本节记录对照源码审校后的已证实结论需注意的边界

11.1 已由源码证实(OH 部分)

结论 源码依据
sandbox: 0SERVICE_ATTR_WITHOUT_SANDBOX,跳过 EnterSandbox //base/startup/init/services/init/init_service_manager.cGetServiceSandbox
const.sandbox=enable 才使服务沙箱生效 //base/startup/init/services/init/standard/init_service.cSetServiceEnterSandbox
/system/bin//vendor/bin/ 前缀会 EnterSandbox 同上 strncmp 判断
服务沙箱 pivot_rootPrepareSandbox,子进程仅 SetNamespace //base/startup/init/services/sandbox/sandbox.c
应用默认沙箱根 /mnt/sandbox/<userId>/<PackageName> //base/startup/appspawn/modules/sandbox/sandbox_utils.cppUID_BASE=200000
应用沙箱默认开关为 ON sandbox_load.cGetBoolValueFromJsonObj(..., "sandbox-switch", true)
appdata-sandbox.json 映射 distributedfiles ↔ HMDFS merge_view //base/startup/appspawn/appdata-sandbox.json mount-paths
begetctl sandbox -b <pid> 进入应用沙箱 //base/startup/init/services/begetctl/sandbox.cpp
板端实测 launcher distributedfiles bind /proc/<pid>/mountinfomerge_view/data/<PackageName>distributedfiles(§9.2.3)
板端实测 sandbox:0 服务可见 hmdfs distributedfiledaemonthingmodel_service 的 mountinfo 含 /mnt/hmdfs/100/account

11.2 产品/环境相关

说明
SELinux 是否启用取决于产品编译与策略;cfg 中 secon 仅在 SELinux 开启时生效
const.sandbox 嵌入式产品可能未 enable;本机实测为 enable
/mnt/hmdfs/ 动态挂载不可见 sandbox:1 + /mntprivate bind 的前提下成立;本机 sandbox:0 服务实测可见 hmdfs
HAP 能否 ioctl HMDFS 除沙箱外还受权限、SELinux、框架封装约束;本文表述为「通常不能/不应直接 ioctl」
hdc shell 的 uid cfg 默认 shell;本机实测 const.debuggable=1 时 hdc shell 与 hdcd 均为 root,但均不进沙箱
remotefileshare NAPI 产品可选;本机镜像未集成 libremotefileshare.z.so
§0 跨平台对比 概要类比,Android/iOS/Windows 等细节因版本而异
相关推荐
youtootech12 小时前
HarmonyOS《柚兔学伴》项目实战25-我的页面、Web 嵌入与项目总结
前端·华为·harmonyos
三声三视14 小时前
uni-app 鸿蒙端传参变成 [object Object]?顺着源码追到 ArkTS router 底层才搞明白
人工智能·ai·uni-app·aigc·ai编程·harmonyos
达子66614 小时前
第7章_HarmonyOS 图解 Ability公共事件与通知
华为·harmonyos
爱写代码的阿森14 小时前
鸿蒙三方库 | harmony-utils之KvUtil键值型数据库操作详解
数据库·华为·harmonyos·鸿蒙·huawei
爱写代码的阿森16 小时前
鸿蒙三方库 | harmony-utils之PreferencesUtil用户首选项读写详解
华为·harmonyos·鸿蒙·huawei
达子66617 小时前
第6章_HarmonyOS 图解 Ability任务调度
华为·harmonyos
FrameNotWork17 小时前
HarmonyOS 6.0 分栏布局与折叠适配
华为·harmonyos
爱写代码的阿森17 小时前
鸿蒙三方库 | harmony-utils之PreferencesUtil首选项数据监听详解
服务器·华为·harmonyos·鸿蒙·huawei
爱写代码的森17 小时前
蒙三方库 | harmony-utils之FileUtil文件重命名与属性查询详解
linux·运维·服务器·华为·harmonyos·鸿蒙·huawei