一个文件,N 个路径:OpenHarmony 路径“分身术“全解
起因是一次设备实测:
ls -l /看到/etc -> /system/etc、/bin -> /system/bin、/lib -> /system/lib,于是改预置应用配置时到底该写/etc/app/...还是/system/etc/app/...,就有了确定答案。顺着往下查才发现,"两个路径"说少了——同一个文件在 OpenHarmony 里挂三四个名字是常态,多的能有五六个。标题里的"不是真身"是说它们都不是物理落点,而是别名或逻辑视图,不是文件不存在。本文以 OpenHarmony 标准系统(5.x / 6.x 主线)为准,涉及版本差异处单独标注。所有结论都建议用第 8 节的命令在自己的设备上复核一次。
1. 先把三类路径分清楚
在设备上 ls -l /,很容易把"路径长得像"都理解成软链接。实际上 OpenHarmony 里"看起来是两份、其实是一份"的路径至少有三类,解析者完全不同:
| 类别 | 谁负责解析 | 典型例子 | 判别方式 |
|---|---|---|---|
| VFS 软链接 | 内核 VFS,编译镜像时写入 | /etc -> /system/etc、/bin -> /system/bin | ls -l 显示 ->,readlink -f 能解析 |
| 挂载点 / 视图映射 | mount、应用沙箱、HMDFS | /data/storage/el2/base → /data/app/el2/100/base/<包名> | mount 输出、官方映射表 |
| URI 逻辑根 | 文件管理服务 | file://docs/storage/Users/currentUser/Download | 只在 API / URI 里存在,shell 里未必有这个目录 |
把第二类、第三类当成第一类,是移植三方库、写调试脚本时最常见的坑:前者能 readlink,后两者不能。
而且这三类可以叠加:一个文件完全可能同时拥有 VFS 别名、挂载视图别名和 URI 别名,于是"一个文件,两个路径"只是最简单的情况。第 2.4 节专门把这件事拆开讲。
2. 第一类:根目录兼容软链接,是打包镜像时就写死的
2.1 为什么需要它
标准系统采用 system as root:真正的根文件系统内容打在 system.img 里,启动时内核把 system 分区当作根挂上(部分板型先由 ramdisk 挂到 /usr,再 switch root)。所以"实体"全在 /system 下。
但 Unix/Linux 几十年下来的约定是配置放 /etc、可执行文件放 /bin、库放 /lib,大量三方库、脚本、构建工具把这些路径硬编码了。在根目录放几条软链接,是成本最低的兼容方案——不需要改任何上游代码。
2.2 源码证据
build/ohos/images/build_image.py 里 _prepare_root() 明确创建这些链接(master 分支):
def _prepare_root(system_path: str, target_cpu: str):
root_dir = os.path.join(os.path.dirname(system_path), 'root')
...
os.symlink('/system/bin', os.path.join(root_dir, 'bin'))
os.symlink('/system/bin/init', os.path.join(root_dir, 'init'))
os.symlink('/system/etc', os.path.join(root_dir, 'etc'))
os.symlink('/vendor', os.path.join(root_dir, 'chipset'))
if target_cpu == 'arm64' or target_cpu == 'riscv64':
os.symlink('/system/lib64', os.path.join(root_dir, 'lib64'))
os.symlink('/system/lib', os.path.join(root_dir, 'lib'))
| 根目录链接 | 目标 | 说明 |
|---|---|---|
/bin | /system/bin | 可执行文件、hdc 侧常用命令 |
/etc | /system/etc | 系统配置,含 app/、init/、param/ 等 |
/lib | /system/lib | 32 位库 |
/lib64 | /system/lib64 | 仅 arm64 / riscv64 生成 |
/init | /system/bin/init | init 进程入口 |
/chipset | /vendor | 芯片组件目录别名,与 manifest 里的 ohos:chipset 分组对应 |
ramdisk 镜像另有一条相对链接:/init -> bin/init_early,用于早期启动阶段(_prepare_ramdisk())。
早期版本(2.x / 3.x)还能看到 product -> /system/product、product_services -> /system/product_services、sdcard -> /storage/self/primary、system/vendor -> /vendor。不同产品配置会增删,以设备上 ls -l / 为准,不要照抄任何一篇文章的清单。
2.3 三条推论
- 不占两份空间:软链接只是一个目录项,
md5sum两个路径得到同一个值,写操作顺着链接落到同一个 inode。 - 写哪条路径效果一样:改
/etc/app/a.json就是改/system/etc/app/a.json。 - “效果一样"不等于"应该用别名”:见下一节。
2.4 不止两个路径:一个文件能挂多少个名字
先给两个能立刻在设备上验的例子。
/system/bin/init 至少有三个路径:
| 路径 | 怎么来的 |
|---|---|
/system/bin/init | 物理文件 |
/init | 根目录软链接 |
/bin/init | /bin -> /system/bin 再拼上文件名 |
/proc/1/exe | 内核为运行中的 1 号进程提供的别名(Linux 通用机制,视权限可访问) |
Download 里的一个 a.txt 也能有四种以上"地址":
| 地址 | 类型 |
|---|---|
/storage/media/100/local/files/Docs/Download/a.txt | 物理路径 |
/storage/Users/currentUser/Download/a.txt | 文件管理器逻辑路径 / Environment 返回值 |
file://docs/storage/Users/currentUser/Download/a.txt | 文档类 URI |
/storage/cloud/100/...(启用云端合并视图时) | bind mount 视图 |
file://media/Photo/<id>/... | 若它是图片,媒体库还有一套独立 URI |
能不断追加路径的机制一共六种:
| 机制 | 是否同一 inode | OpenHarmony 里的例子 | 限制 |
|---|---|---|---|
| 多个软链接指向同一目标 | 是 | /init、/bin/init 都落到 /system/bin/init;/vendor、/chipset、/system/vendor(早期)都指向 vendor 分区 | 目标被删则全部失效 |
| 软链接链 | 是 | 早期 /sdcard -> /storage/self/primary,后者再指向实际视图 | 层级过深会 ELOOP |
硬链接 ln | 是,且是平等的目录项 | ext4 / f2fs 分区可用 | 不能跨文件系统、不能对目录;HMDFS 上返回 Operation not permitted(内核 hmdfs_dir_inode_ops_local 未提供 .link) |
| bind mount | 是(同一挂载实例) | storage_service 的 HmdfsTwiceMount() 建三条 bind mount:device_view → /storage/media/%d/、cloud_merge_view → /storage/cloud/%d/、本地 Docs → 云端 Docs | 随用户登录 / 注销挂载、卸载 |
| FUSE / merge 视图 | 逻辑上同一份数据 | /mnt/data/<userId>/media_fuse/Photo、/mnt/hmdfs/<userId>/account/merge_view/ | POSIX 语义被裁剪,不能当普通 ext4 用 |
| URI 别名 | 不是 inode,是服务标识 | file://docs/...、file://media/...、Picker 返回的临时授权 URI | 只在 API 内有效,格式可能随版本变 |
另外 /dev/block/by-name/* 这类块设备软链接,是启动阶段 ueventd 为同一个设备节点批量创建的别名,也是"一个设备多个路径"的典型。
还有两个容易误判的边界:
- 内容相同 ≠ 同一文件。
md5sum只证明字节一样,cp出来的副本是另一个 inode。 - HMDFS 大小写不敏感:
a.txt和A.txt被视为同一个文件,等于凭空多一个路径名;解压含这类命名冲突的压缩包会互相覆盖。
反向陷阱也要记住:currentUser 是"一个逻辑路径 → 多个物理文件"(切换用户后指向不同 userId 的目录),Docs/Documents 在不同用户下也是不同实体。方向和"多个路径 → 同一文件"正好相反,别混为一谈。
判别是不是同一份,按这个顺序:
hdc shell readlink -f <path> # 解析软链接链
hdc shell stat -L <path> # 看 Inode 值,一致才是同一份
hdc shell ls -Li <pathA> <pathB> # -L 跟随链接,-i 打印 inode
hdc shell mount | grep -E 'hmdfs|media|cloud' # 看 bind mount / FUSE 视图
不同设备 toybox 参数略有差异,stat 不支持时用 ls -Li 兜底;md5sum 只作辅助证据。
3. 为什么改系统配置时坚持写真实路径
以 BMS(包管理服务)的预置应用配置为例,实操流程:
hdc shell mount -o remount,rw /
hdc file send install_list_permissions.json /system/etc/app/install_list_permissions.json
hdc shell reboot
注意两点:install_list_permissions.json 里的权限预授予按应用指纹匹配,重装对应应用后才会生效;改完先 readlink -f 确认落点,再 cat 校验内容,最后重启。
统一写 /system/etc/app 而不是 /etc/app,理由有三个:
- 它是真实挂载路径。BMS 开机扫描
install_list_permissions.json、install_list.json这类预置配置用的是/system/etc/app;对代码、看日志、写文档时,用真实路径不会产生歧义。 - 避开工具行为差异。个别工具在写入软链接路径时行为不一致(有的先删目标再重建,可能把链接本身替换成普通文件或目录),统一用真实路径最稳妥。
- 备份链条清晰。本地备份的"原本 json"和设备上的活动文件,都按物理路径比对,才能保证是同一来源。
4. 第二类:/storage/media/100/local/files/Docs 是物理路径
这是 DevEco 的 Device File Browser 里"普通文件视图"显示的真实物理路径。逐段拆开:
| 片段 | 含义 |
|---|---|
/storage/media | 公共存储根目录 |
100 | userId,主用户从 100 开始递增;多用户 / 应用分身场景下会变 |
local | 本地设备存储,区别于分布式 / 云端视图 |
files | 用户文件区 |
Docs | 文档类公共目录,其下放 Desktop、Documents、Download 等 |
官方文档写得很直白:“用户的桌面、文档、下载等公共目录位于 /storage/media/100/local/files/Docs 路径下”;模拟器里拖拽上传的文件也落在 /storage/media/100/local/files/Docs/Download/。
版本差异要留意:
- 3.2 / API 9 时代媒体库路径是平铺的:
/storage/media/100/local/files/{Pictures,Audios,Videos,Camera,Documents,Download}。 - 新版本把文档类收拢到
Docs/下,变成Docs/Documents、Docs/Download两级。别把Docs和Documents当成一个目录。 - 从 API 22 起新增
/mnt/data/100/media_fuse/Photo,原/storage/media/100/local/files/Photo变为指向它的链接。按老路径找图库文件会扑空。
5. 那么 /storage/Users/currentUser/Download/ 到底是什么
结论先给:它不是根目录那种 VFS 软链接,而是"用户文件命名空间里的逻辑根 + 当前用户占位符",由文件管理服务与 API 在运行时解析,不是由内核 symlink 解析。
5.1 三层拆解
第一层:URI 层。 官方《用户文件 URI 介绍》定义文档类 URI 格式为:
file://docs/storage/Users/currentUser/<相对路径>/<文件名>
其中 file://docs/storage/Users/currentUser/ 被定义为"文件管理器的根目录",相对路径举例 Download/ 和 Documents/。这里的 docs 是 URI 的 authority,不是磁盘上的一个目录。官方还特别提醒:不建议解析 URI 中的片段用于业务代码开发。
第二层:逻辑根层。 ExternalFileManager 通过 getRoots() 暴露若干逻辑根:
| 逻辑根 | 含义 |
|---|---|
/storage/Users/currentUser | 当前用户文档目录(下载、文档等) |
/storage/Share | 共享磁盘 |
/storage/External/{卷名} | U 盘等外置存储,插入后动态出现 |
/storage/hmdfs/{节点名} | 分布式文件(HMDFS),视系统启用情况 |
currentUser 就是"当前活跃用户"的别名:主用户下等价于 100,切到多用户 / 访客时解析到别的 userId。这正是不能硬编码 100 的原因。媒体库则是独立根(authority = media),不在这份列表里。
第三层:物理层。 由文件管理服务 / Environment 接口完成落点映射。华为开发者论坛上有实测日志:Environment.getUserDownloadDir() 返回 /storage/Users/currentUser/Download/,对应的文件夹正是 Device File Browser 中的 /storage/media/100/local/files/Docs/Download。
该接口需 SystemCapability.FileManagement.File.Environment.FolderObtain 能力,早期主要支持 2in1 设备,从 API 26 起支持 tablet,且只返回路径、不代申请权限,访问公共目录仍需申请 Download / Documents / Desktop 目录权限。
5.2 映射速查
| 应用 / URI 里看到的 | 物理落点(主用户) | 说明 |
|---|---|---|
file://docs/storage/Users/currentUser/Download/a.txt | /storage/media/100/local/files/Docs/Download/a.txt | 文档类 URI |
/storage/Users/currentUser/Download/ | /storage/media/100/local/files/Docs/Download/ | getUserDownloadDir() 返回值 |
/storage/Users/currentUser/Documents | /storage/media/100/local/files/Docs/Documents | 注意是 Docs/Documents 两级 |
file://media/Photo/<id>/... | /mnt/data/100/media_fuse/Photo(API 22+) | 媒体库独立根 |
5.3 为什么说它"不是软链接"
- 第 2 节那份构建脚本里没有
/storage/Users这条链接——rootfs 软链接是编译期写进镜像的,而currentUser是运行时概念。 - 在不少开发板 / 标准系统设备上
/storage本身是 tmpfs,/storage/Users默认不存在。Harmonybrew 的安装文档就要求手动mkdir -p /data/storage/Users/currentUser再ln -s /data/storage/Users /storage/Users,才能让$HOME之类的工具跑起来,且重启后要重建。 - 在 PC / 2in1 形态上,系统把 HMDFS 挂到用户家目录,
$HOME才指向/storage/Users/currentUser,此时它表现为一个可访问的目录视图。HMDFS 裁剪了 POSIX 语义:文件名大小写不敏感、不支持硬链接与 UDS、chmod表现与 ext4 不同——移植底层软件时别把它当普通 ext4 用。 - 官方明确"不建议解析 URI 片段",正说明它是服务端维护的逻辑标识,不是稳定的物理路径。
一句话总结:/storage/Users/currentUser/... 是给用户和应用看的地址,/storage/media/<userId>/local/files/... 是给内核和 hdc 看的地址。
6. 第三类:应用沙箱路径是"映射",不是软链接
应用代码里 context.filesDir 拿到的是固定的沙箱路径,系统通过挂载 / 路径转换映射到带 userId 和包名的物理目录:
| 沙箱路径 | 真实物理路径 |
|---|---|
/data/storage/el1/bundle | /data/app/el1/bundle/public/<包名> |
/data/storage/el1/base | /data/app/el1/<userId>/base/<包名> |
/data/storage/el2/base | /data/app/el2/<userId>/base/<包名> |
/data/storage/el1/database | /data/app/el1/<userId>/database/<包名> |
/data/storage/el2/database | /data/app/el2/<userId>/database/<包名> |
/data/storage/el2/distributedfiles | /mnt/hmdfs/<userId>/account/merge_view/data/ |
el1 是设备级加密(开机即可读),el2 是用户级加密(解锁后才可读),context.filesDir 默认指向 el2。
两个提醒:userId 和包名都会变化,不要在代码里硬编码物理路径;想看沙箱视图要用 hdc shell -b <bundleName> ls -l ./data/storage/el2/base,直接 hdc shell ls /data/storage/el2/base 看不到同一个东西。
7. 真身路径清单:哪些是确定的物理落点
前面都在讲别名,这一节反过来,把能坐实的真身汇总一遍。判定标准三条,全满足才算"确定":
- 它本身是挂载点,或位于挂载点之下的真实目录(
mount能看到它所在的设备); readlink -f <别名>的结果指向它,而不是它指向别人;- 官方文档 / 源码以"物理路径"“真实路径”"安装目录"称呼它。
下文 <userId> 主用户为 100(多用户递增),<包名> 为 bundleName,<hardware> 来自 bootargs。
7.1 分区挂载点:最硬的真身
| 挂载点 | 来自 | 说明 |
|---|---|---|
/ | system.img | 无 ramdisk 的 system as root 场景,system 分区直接作根(bootargs root=/dev/mmcblk0pN) |
/usr | system.img | 两阶段启动的中间挂载点,一阶段 fstab 标 required,随后 SwitchRoot 成为新根 |
/vendor | vendor.img | required 分区,板级 fstab 与 init 配置所在 |
/sys_prod、/chip_prod | 同名分区 | 产品 / 芯片扩展分区,预置配置分层优先级更高 |
/chip_ckm、/misc | 同名分区 | 视产品配置出现(部分 6.1 板型 fstab 可见) |
/data | userdata(ext4 / f2fs,加密) | 用户数据、应用数据分区 |
/mnt/... | 运行时挂载 | HMDFS、Cloud FUSE、Media FUSE、ShareFS、外置存储,见 7.3 |
唯一要打问号的一条:system 分区究竟挂在 /、/usr 还是 /system,随启动方式(有无 ramdisk)和 fstab 写法变化,官方 fstab 示例与板型实测并不一致。连"真身"都要现场看 mount 才能定,别照抄任何一张表。
7.2 系统配置与程序:改动时该写的那一侧
| 真身路径 | 里面是什么 | 常见别名 / 逻辑入口 |
|---|---|---|
/system/etc/app/ | BMS 预置应用 5 个 JSON:install_list.json、install_list_capability.json、install_list_permissions.json、default_app.json、uninstall_list.json | /etc/app/ |
/system/app/<包名>/ | 预置 HAP 安装包 | — |
/vendor/etc/app/、/sys_prod/etc/app/、/chip_prod/etc/app/ | 分层覆盖配置,层名 system → chipset → sys_prod → chip_prod 优先级递增(param get const.cust.config_dir_layer 可查) | /chipset/etc/app/(/chipset -> /vendor) |
/system/etc/init.cfg、/system/etc/init/*.cfg | init 服务与任务配置 | /etc/init... |
/vendor/etc/init.<hardware>.cfg、/vendor/etc/fstab.<hardware> | 板级 init 配置、扩展分区表 | — |
/system/etc/param/*.para、/system/etc/param/ohos_const/*.para、/vendor/etc/param/*.para | 系统参数,加载顺序:OS 固定值 → vendor → system | /etc/param/:官方说"安装到 /etc/param",因 /etc 是软链,实体在 /system/etc/param/ |
/system/etc/passwd、/system/etc/group | UID / GID 定义 | /etc/passwd、/etc/group |
/system/bin/、/system/lib/、/system/lib64/ | 可执行文件与库 | /bin、/lib、/lib64、/init |
/vendor/etc/、/vendor/lib64/ | 厂商 HAL 与配置 | /chipset/... |
顺带一个印证:syspara 文档里写 .para 文件"安装到 /etc/param/",而 GN 的 module_install_dir = "etc/param" 是相对 system 分区的路径——文档里的 /etc 其实已经被软链接折回 /system/etc。这正是全文主题在官方文档里留下的痕迹。
7.3 用户文件:公共目录与运行时挂载
| 真身路径 | 说明 | 常见别名 / 逻辑入口 |
|---|---|---|
/storage/media/<userId>/local/files/Docs/{Desktop,Documents,Download} | 文档类公共目录物理落点,DevEco Device File Browser 显示的就是它 | /storage/Users/currentUser/...、file://docs/... |
/storage/media/<userId>/local/files/{Pictures,Audios,Videos,Camera,Documents,Download} | 3.2 / API 9 的平铺布局 | 老版本 mediaLibrary 路径 |
/mnt/data/<userId>/media_fuse/Photo | API 22+ 媒体库实体 | /storage/media/100/local/files/Photo(现为链接)、file://media/... |
/mnt/hmdfs/<userId>/device_view/local、/mnt/hmdfs/<userId>/merge_view | HMDFS 本地视图 / 多设备合并视图 | /storage/hmdfs/{节点名} |
/mnt/data/<userId>/cloud_fuse、/mnt/data/<userId>/cloud | Cloud FUSE 两处挂载 | /storage/cloud/<userId>/ |
/mnt/share/<userId> | ShareFS 挂载(源 /data/service/el2/<userId>/share) | /storage/Share |
/mnt/data/external/{UUID} | U 盘实际挂载点 | /storage/External/{卷名} |
7.4 应用数据:沙箱视图背后的落点
| 真身路径 | 说明 | 对应沙箱视图 |
|---|---|---|
/data/app/el1/bundle/public/<包名> | 应用安装包目录 | /data/storage/el1/bundle |
/data/app/el{1,2}/<userId>/base/<包名> | el1 / el2 级加密数据目录 | /data/storage/el{1,2}/base |
/data/app/el{1,2}/<userId>/database/<包名> | 加密数据库目录 | /data/storage/el{1,2}/database |
/mnt/hmdfs/<userId>/account/merge_view/data/ | 账号级分布式融合目录 | /data/storage/el2/distributedfiles |
/data/local/tmp/ | 调试暂存区,hdc 推送常用 | — |
/data/log/hilog/、/data/log/faultlog/faultlogger | 历史日志、崩溃日志 | — |
7.5 明确"不算真身"的对照表
| 路径 | 为什么不算 | 真身 / 解析去向 |
|---|---|---|
/etc、/bin、/lib、/lib64、/init、/chipset | 编译镜像时写入的 VFS 软链接 | /system/...、/vendor |
/sdcard、/product、/product_services、/system/vendor | 早期版本软链接,新版本可能已移除 | /storage/self/primary、/system/product、/vendor |
/storage/Users/currentUser、/storage/Share、/storage/External/{卷名}、/storage/hmdfs/{节点名} | 文件管理器 getRoots() 的逻辑根 | 7.3 各物理落点 |
file://docs/...、file://media/... | URI,只在 API 语境里有效 | Picker / fileAccess / photoAccessHelper 解析 |
/data/storage/el{1,2}/... | 应用沙箱视图 | 7.4 各物理落点 |
/dev/block/by-name/*、/dev/block/platform/<控制器>/by-name/* | ueventd 给设备节点批量建的别名 | /dev/block/mmcblkXpY |
7.6 依据等级与必须现场复核的部分
| 等级 | 覆盖范围 |
|---|---|
| A:源码 / 官方文档直给 | 分区挂载点与 fstab、/system/etc/app/ 5 个 JSON、/system/etc/init/、/system/etc/param/、/system/bin/ 与 /system/lib/、应用沙箱映射表、/storage/media/.../Docs、/mnt/data/<userId>/media_fuse/Photo、storage_service 的 bind mount |
| B:官方社区实测 / Device File Browser 文档 | getUserDownloadDir() 与 Docs/Download 的对应、模拟器上传落点、早期平铺布局 |
必须现场复核的六项:system 分区的实际挂载点;Docs 布局(3.2 平铺 vs 新版 Docs/);Photo 链接是否指向 media_fuse;userId 是否为 100;云 / Share / HMDFS 挂载是否启用;文件系统类型(ext4 / f2fs / erofs,部分新版本只读分区无法 remount,rw,需 overlay 或重刷)。
hdc shell mount
hdc shell mount | grep -E '/(system|vendor|data|mnt|storage|usr)'
hdc shell readlink -f /etc/app/install_list_permissions.json
hdc shell param get const.cust.config_dir_layer
hdc shell ls -l /system/etc/app/
8. 在设备上自己复核
# 1 根目录软链接
hdc shell ls -l /
hdc shell readlink -f /etc/app/install_list_permissions.json
hdc shell md5sum /etc/app/install_list_permissions.json /system/etc/app/install_list_permissions.json
hdc shell ls -Li /etc/app/install_list_permissions.json /system/etc/app/install_list_permissions.json
# 2 挂载点
hdc shell mount | grep -E 'system|vendor|data|hmdfs|media'
# 3 公共目录
hdc shell ls -l /storage/media/100/local/files/
hdc shell ls -l /storage/media/100/local/files/Docs/
# 4 currentUser 在 VFS 里到底存不存在
hdc shell ls -l /storage/Users/ ; echo "exit=$?"
# 5 沙箱视图
hdc shell -b <bundleName> ls -l ./data/storage/el2/base
判别口诀:readlink 能解析 → VFS 软链接;mount 里出现 → 挂载点;只在 URI / API 里出现 → 逻辑视图。多个路径是否同一份看 inode(ls -Li);同一个逻辑路径是否指向不同文件,看 currentUser / 多用户场景。
9. 常见踩坑清单
| 现象 | 原因 | 处理 |
|---|---|---|
hdc file send 到 /storage/Users/currentUser/Download/ 失败 | 该路径在 VFS 里不一定存在 | 用物理路径 /storage/media/100/local/files/Docs/Download/,或用 Picker / URI |
| 换用户、开应用分身后文件"丢了" | 硬编码了 100 | 用 currentUser / Environment 接口动态取路径 |
从 /etc 推送配置后软链接被替换成普通目录 | 个别工具对软链接路径先删后建 | 一律推到 /system/etc/... |
| 解析 user file URI 拼路径后在新版本失效 | URI 是逻辑标识,格式可能变 | 只把 URI 交给 fs / fileAccess 使用 |
| 按老路径找不到图库文件 | API 22 起 Photo 链接指向 media_fuse | 用 file://media/... 或 mediatool 查询 |
混淆 Docs 与 Documents | 新版本是 Docs/Documents 两级 | 先 ls -l 看目录树 |
remount,rw / 后仍写不进去 | 机型 / 版本的挂载点与只读策略不同 | 先看 mount 确认实际挂载点和文件系统类型 |
以为"改了 /etc 就只有两处要同步" | bind mount、云合并视图、URI 缓存都可能另有入口 | 以物理路径为唯一真源,用 inode 校验,别逐个别名去改 |
在 HMDFS 家目录下 ln 硬链接失败 | HMDFS 未实现 .link,返回 Operation not permitted | 改用拷贝 / 重命名,或把工作目录放到 ext4 分区 |
10. 小结
/etc、/bin、/lib是构建脚本写进镜像的 VFS 软链接,和/system/...是同一个 inode,不占两份空间;改配置写真实路径最稳。- "两个路径"只是最小值。软链接、软链接链、硬链接、bind mount、FUSE 视图、URI 别名可以叠加,
/system/bin/init至少三个路径,用户文件常有四五种地址。判同一份要看 inode,不是看md5sum。 /storage/media/100/local/files/Docs是公共目录的物理落点,100是 userId,Docs下才是Desktop/Documents/Download。/storage/Users/currentUser/Download/是文件管理器的逻辑根 + 当前用户别名,由服务在运行时解析,不是内核软链接;它和Docs/Download是"同一个用户文件的两种地址",而不是"两份文件"。反向还要注意:currentUser在不同用户下指向不同物理文件。- 应用沙箱路径属于第三类映射,靠 userId + 包名落地,别硬编码。
- 真身清单见第 7 节:分区挂载点、
/system/etc/...、/storage/media/<userId>/...、/data/app/...是四类确定的物理落点;唯一连真身都要现场确认的是 system 分区的挂载点(/、/usr、/system三种写法都存在)。
11. 参考资料
- OpenHarmony build 仓:
ohos/images/build_image.py(_prepare_root/_prepare_ramdisk),https://github.com/openharmony/build/blob/master/ohos/images/build_image.py - OpenHarmony docs《User File URI》:https://github.com/openharmony/docs/blob/master/en/application-dev/file-management/user-file-uri-intro.md
- 《用户文件 URI 介绍》:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/user-file-uri-intro
- 《访问设备文件》(公共目录 / 媒体库目录说明):https://device.harmonyos.com/cn/docs/apiref/harmonyos-guides/ide-device-file-explorer
- 《应用沙箱目录与应用沙箱路径》:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-sandbox-directory-0000002213459197
- OpenHarmony 论坛《通过 fileAccess 模块获取的文件路径对应的实际路径是什么》:https://forums.openharmony.cn/forum.php?mod=viewthread&tid=475
- 华为开发者论坛:
getUserDownloadDir()与Docs/Download对应关系实测帖:https://developer.huawei.com/consumer/cn/forum/topic/0201169470882540332 - 启动恢复 / fstab(system as root、两阶段 init):https://seaxiang.com/blog/lsG31L
filemanagement_storage_serviceMount Management(HmdfsTwiceMount()的三条 bind mount、Cloud FUSE、Media FUSE):https://deepwiki.com/openharmony/filemanagement_storage_service/2.3-mount-managementfilemanagement_user_file_serviceURI System and Path Management(逻辑根与 URI 构造、路径归一化):https://deepwiki.com/openharmony/filemanagement_user_file_service/2.3-uri-system-and-path-management- 《鸿蒙 PC 底层开发技术详解(六):HMDFS 对我们的影响》(大小写不敏感、不支持硬链接 / UDS):https://harmonypc.csdn.net/69f8a31c0a2f6a37c5a7d4bd.html
- openEuler《hmdfs 分布式文件系统概述》(
device_view/merge_view双视图):https://docs.openeuler.openatom.cn/zh/docs/22.03_LTS_SP4/server/development/distributed/hmdfs_distributed_file_system_overview.html - 官方《整机启动流程》(fstab 挂载点
/usr、/vendor、/sys_prod、/chip_prod、/data,system as root):https://gitcode.com/openharmony/docs/blob/8858bc1e13c7b02373e23735bf8cd96c6e278ce8/zh-cn/device-dev/subsystems/./subsys-boot-deviceboot.md - 官方《预置应用配置指南》(
/system/etc/app/install_list.json、分层配置目录const.cust.config_dir_layer):https://seaxiang.com/blog/80834c76497d423186a88bcdbb07926e - 《OpenHarmony 预置应用配置详解:
/system/etc/app/下的 5 个 JSON》:https://openharmonycrossplatform.csdn.net/6ad0852c49ed150a32d7d7b3.html - 《syspara 系统属性组件》(
.para安装到/etc/param、参数加载顺序):https://device.harmonyos.com/cn/docs/documentation/guide/subsys-boot-syspara-0000001063362360 startup_initFstab Mounting(fstab 位于/vendor/etc/fstab.${ohos.boot.hardware}、两阶段挂载):https://deepwiki.com/openharmony/startup_init/5.1-fstab-mounting
| | |
| — | — |developtools_integration_verificationStartup Validation(/system/etc/init、/vendor/etc/init、/system/etc/passwd|group扫描路径):https://deepwiki.com/openharmony/developtools_integration_verification/7-startup-validation-system
更多推荐


所有评论(0)