Windows + Git Bash 下用 hdc 推文件到鸿蒙应用私有沙箱的四个连环坑
关键词:HarmonyOS / OpenHarmony、hdc、file send、EL2 沙箱、MSYS_NO_PATHCONV、SELinux、root 设备 适用读者:用 Windows + DevEco SDK 工具链做鸿蒙应用调试的开发者,遇到过 调试鸿蒙应用时经常需要把一份准备好的测试数据(缓存、配置、mock 响应等)预置到自家应用的 EL2 私有沙箱里,让 App 启动时读这份预置数据走特定分支。 听起来就是一条 假设你已经满足前置条件(自家 debug App、已 root 设备或 userdebug 固件)。 现象 仔细看报错里的路径: 原因:hdc 对本地文件参数做了 CWD 相对路径解析,但没处理"参数已经是绝对路径"这种分支,Windows 盘符根( 解法:先 跨过坑 1 后你会遇到更诡异的: 现象 设备端的 原因:Git Bash 基于 MSYS2,它对所有"看起来像 Unix 绝对路径"(以 hdc 的设备端路径跟本地路径长得一模一样(都以 解法:用 其他等价方案: 推荐 跨过坑 2 后,如果你的设备是正式发布固件,还会撞上: 现象 原因: 解法:只有两条路 走 方案 2 不需要 root 但需要你的 App 配合一段"从 tmp 拷贝到沙箱"的调试代码。生产发布前务必用构建类型门控掉这段代码。 假设换到了 root 设备或 userdebug 固件, 单看 期望输出: 反例(非 root 或假 root): 三条里任一条不过 = 不能信其写沙箱的能力,换固件。 另一常见分区是 推文件前看一眼你的 App 实际读的是哪个子目录,别推错地方。 建议不管哪种情况都显式修一遍: 权限给 换好下面 6 个变量,整块粘贴即可:Windows + Git Bash 下用 hdc 推文件到鸿蒙应用私有沙箱的四个连环坑
hdc file send 报 permission denied / no such file or directory 不知道卡在哪一层的同学。前言
hdc file send 的事。实际做下来在 Windows + Git Bash 环境下会踩到 4 个独立的坑连成一串,每一坑的报错看起来都像是另一个问题,换方向排查半天才摸清楚根因。本文把整条路径一次讲清,给出最终可用命令和一眼排查表。前置条件 & 适用范围
一、最终可用命令(TL;DR)
# 0. 先设好环境变量,避免后面命令冗长(你的实际 SDK 路径)
export DEVECO_SDK_HOME="/path/to/your/DevEco/sdk"
export HDC="$DEVECO_SDK_HOME/default/openharmony/toolchains/hdc.exe"
# 1. cd 到源文件所在目录(不能用绝对路径喂给 hdc — 见坑 1)
cd "C:/Users/<YourName>/Downloads"
# 2. 推文件(禁用 MSYS 路径转换 — 见坑 2)
MSYS_NO_PATHCONV=1 "$HDC" file send \
"data.json" \
"/data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json"
# 3. 校正 owner/权限为目标应用 uid(查自己的 uid 方法见下文)
MSYS_NO_PATHCONV=1 "$HDC" shell "
chown <APP_UID>:<APP_UID> /data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json &&
chmod 660 /data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json &&
ls -la /data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json
"<APP_UID> 查法:MSYS_NO_PATHCONV=1 "$HDC" shell "ls -ld /data/app/el2/100/base/com.example.myapp/haps/entry/files"
# drwxrwx--- 2 20020124 20020124 ...
# └─────┬────┘
# 应用 uid:gid(每台设备、每个应用都不同)二、四个连环坑(按遇到顺序)
坑 1:hdc 把 Windows 绝对路径拼接到当前工作目录后面
hdc file send "C:/Users/Alice/Downloads/data.json" "/data/..."
# [Fail] Error opening file: no such file or directory,
# path:D:\your\current\workdir\C:/Users/Alice/Downloads/data.jsonD:\当前工作目录\C:/Users/Alice/... — hdc 把已经是绝对路径的本地参数又拼在了 CWD 后面,形成 Windows 根本识别不了的畸形路径。C:、D:)的引入也加剧了这个问题。cd 到源文件目录,用相对路径传给 hdc:cd "C:/Users/Alice/Downloads"
hdc file send "data.json" "/data/..."同样的问题也出现在
hdc install,官方文档没明说但踩过的人都知道。坑 2:Git Bash 的 MSYS 路径转换把设备端路径改坏了
cd "C:/Users/Alice/Downloads"
hdc file send "data.json" "/data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json"
# [Fail] open path:C:/Program Files/Git/data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json/data/app/... 变成了 Windows 上的 C:/Program Files/Git/data/app/...。/ 开头)的命令行参数会自动做一次路径翻译,猜测这是 POSIX 挂载点,把前缀替换成对应的 Windows 路径。C:/Program Files/Git/ 是 Git for Windows 的安装根。/ 开头),所以也被一视同仁地翻译了,结果变成了一个无效的本地路径。MSYS_NO_PATHCONV=1 临时关闭路径转换:MSYS_NO_PATHCONV=1 hdc file send "data.json" "/data/app/.../data.json"//data/app/...(MSYS 遇到 // 会保留原样)MSYS_NO_PATHCONV=1,显式、局部、可 grep,后来人读脚本一看就知道为什么加它。坑 3:
/data/app/el2/... 是应用私有沙箱,普通 hdc shell 碰不到hdc shell "id"
# uid=2000(shell) gid=2000(shell) ... context=u:r:sh:s0
hdc file send data.json /data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json
# [Fail] permission denied
hdc shell "ls /data/app/el2/100/base/com.example.myapp"
# ls: ...: Permission denied(连目录存在都看不到)uid=2000(shell)、SELinux 域 u:r:sh:s0 运行/data/app/el2/<用户 ID>/base/<包名>/ 是每个应用的 EL2 加密私有目录,权限通常是 drwxrwx---,owner 是应用自己的 uid(例如 20020124),shell 既不在 owner 也不在 group,DAC 直接拒su 提权,which su 没有、param get const.debuggable 也失败,说明是发布固件,没有提权路径/data/local/tmp/ 中转 + 应用内拷贝:hdc file send 到 /data/local/tmp/(shell 可读写)/data/local/tmp/data.json 再写到自家 filesDir坑 4:如何"真·验证 root"(不能只看 uid)
hdc shell "id" 显示 uid=0(root) 就能信吗?不能。存在两类"假 root":id 不够。用三重交叉证据法:hdc shell "id; ls -ld /data/app/el2/100/base/com.example.myapp/haps/entry/files"uid=0(root) gid=0(root) ... context=u:r:su:s0 ← 证据 1 + 2
drwxrwx--- 2 20020124 20020124 ... /data/app/.../files ← 证据 3证据 含义 为什么不能只看这一项 uid=0(root)进程凭据是 root uid 可被改,SELinux 会拦 context=u:r:su:s0SELinux 域是 su(不是 sh)内核 MAC 层也认这个 root,才有实际越权能力 ls -ld 能列出私有沙箱实战穿透 DAC 权限位 前两条过了但仍可能是受限 root(只给部分 cap) uid=2000(shell) ... context=u:r:sh:s0 ← 纯 shell 用户
ls: ...: Permission denied
或
uid=0(root) ... context=u:r:sh:s0 ← uid 像 root 但 SELinux 仍是 sh 域
ls: ...: Permission denied三、沙箱路径解读
/data/app/el2/100/base/com.example.myapp/haps/entry/files/
│ │ │ │ │ │ │
│ │ │ │ │ │ └─ Context.filesDir 对应的目录
│ │ │ │ │ └─────── HAP 模块名(entry 是主模块默认名)
│ │ │ │ └──────────── haps 下按模块分
│ │ │ └───────────────────────────────── 应用包名
│ │ └────────────────────────────────────── base = 应用基础沙箱
│ └─────────────────────────────────────────── 用户 ID(主用户 = 100)
└─────────────────────────────────────────────── el2 = 二级加密分区(锁屏解锁后可访问)el1(开机即可访问,适合系统级数据),应用自己的数据通常落在 el2。其他 Context 子目录:files/ ↔ context.filesDircache/ ↔ context.cacheDirpreferences/ ↔ SharedPreferences(SPUtils 类落地位置)database/ ↔ @kit.ArkData 关系数据库四、owner / 权限修正
hdc file send 推过去的文件,不同版本 hdc / 固件表现不一样:<APP_UID> 运行)读不到# 先查应用 uid(目标 files 目录的 owner 就是)
hdc shell "ls -ld /data/app/el2/100/base/com.example.myapp/haps/entry/files"
# drwxrwx--- 2 20020124 20020124 ...
# 按此修复新推文件
hdc shell "
chown 20020124:20020124 /data/app/.../data.json &&
chmod 660 /data/app/.../data.json
"660 是因为应用访问沙箱时以它自己 uid + 同名 gid 访问,其他用户不需要读写权限。不要图省事给 666 或 777,违反最小权限原则。五、可复用命令模板
# ---- 配置 ----
LOCAL_DIR="C:/Users/<YourName>/Downloads" # 本地文件所在目录
LOCAL_FILE="data.json" # 本地文件名
TARGET_PKG="com.example.myapp" # 你的 debug 应用包名
TARGET_MODULE="entry" # HAP 模块名,默认 entry
TARGET_USER="100" # 多用户系统里的用户 ID,主用户 100
HDC="/path/to/DevEco/sdk/default/openharmony/toolchains/hdc.exe"
TARGET_DIR="/data/app/el2/${TARGET_USER}/base/${TARGET_PKG}/haps/${TARGET_MODULE}/files"
TARGET_PATH="${TARGET_DIR}/${LOCAL_FILE}"
# ---- 1. 验证设备是 root + 目标目录可访问 ----
MSYS_NO_PATHCONV=1 "$HDC" shell "id; ls -ld ${TARGET_DIR}"
# 期望:uid=0(root) ... context=u:r:su:s0 + 能列出目录详情(记下里面的 uid)
# 把上一步查到的 uid 填到这里
TARGET_UID="20020124"
# ---- 2. 推文件 ----
cd "$LOCAL_DIR" && \
MSYS_NO_PATHCONV=1 "$HDC" file send "$LOCAL_FILE" "$TARGET_PATH"
# ---- 3. 校正 owner/权限并验证 ----
MSYS_NO_PATHCONV=1 "$HDC" shell "
chown ${TARGET_UID}:${TARGET_UID} ${TARGET_PATH} &&
chmod 660 ${TARGET_PATH} &&
ls -la ${TARGET_PATH}
"六、一眼失败排查表
报错片段 落在哪一坑 修复 open path:<CWD>\C:/... 本地路径被拼在 CWD 后面坑 1 cd 到源目录,用相对路径open path:C:/Program Files/Git/data/... 设备端路径被改成本地路径坑 2 命令前加 MSYS_NO_PATHCONV=1permission denied + id 显示 uid=2000(shell)坑 3 换 userdebug 固件/root 开发机,或走 /data/local/tmp/ 中转uid=0(root) 但 ls 仍 Permission denied坑 4 假 root / 受限 root(SELinux 域不是 su),换真正的 userdebug 固件文件推过去了但应用读报错 owner/权限 chown <APP_UID>:<APP_UID> + chmod 660应用读到空 / 旧数据 EL2 未解锁 设备重启后要先解锁屏幕,EL2 分区才挂载 七、延伸阅读
/data/app/el1 vs /data/app/el2:EL1 开机即可用,EL2 需锁屏解锁后才挂载。重启设备后如果一直没解锁,所有 EL2 路径都访问不到。hdc 的命令全集和选项:查你本地 SDK 里 toolchains/ 下的 hdc.exe --help。免责声明
com.example.myapp 是占位符,请替换为你自己开发的、debug 签名的 App 包名。不要尝试用这套流程写其他厂商的系统应用或第三方 App 的沙箱 —— 在正式固件上这会被 OS 拒掉,在开发机上虽然可能成功但不符合应用隔离的设计意图,也可能违反相关使用条款。/data/local/tmp/ → 自家沙箱中转方案都只能用于 debug 构建;生产发布前用构建类型(BuildProfile / product / flavor)彻底移除调试通路。