fix(launcherd): M42 包安装入口改委托 nyai — 删内联解包/验签/部署,复用 M35 安全链
- app_launcher.c 删掉 meow_pkg_install 旧内联实现(文件名推包名、硬要 .mpk 后缀、 rm -rf 目标目录直解、失败不回滚 .bak_、不拒 ./..、系统包大小写敏感比较); 导出保留签名逻辑不动(验签仍只 shell-out meow_verify)。 - 新增 pkg_install.c:fork+exec /usr/bin/nyai install <路径>(argv 直传不经 shell; MEOW_NYAI_BIN 覆写,缺省不走 PATH)。包身份/暂存/清理/防降级/大小写全由 nyai 闭环。 - stdout/stderr 分两条管道收:nyai 进度是缓冲 stdout,错误是即时 stderr,合流会把 真错误顶掉(真机首测即踩到);错误文案只取 stderr 尾行。 - start/poll 两段式(+busy 查询)供 UI 主循环按帧驱动不假死;同步封装 20ms 切片轮询, 180s 超时 SIGTERM→SIGKILL,不用时钟 ABI(M40 time32 教训)。 - main_menu --pkg-install 失败打 stderr 可读文案、退出码统一 0/1。 - test_pkg_install.c:44 断言(路径预检含任意文件名可装/目录/不可读/控制字符/超长、 错误文案矩阵、argv 直传、start 立即返回、并发拒装、超时、nyai 缺失)。 - 重链 meow_launcherd 894940B/cea0a735(链 meow_ui.a 86800633,0 warning); fw_content.conf 同步钉值。
This commit is contained in:
1 parent
ebd64d07e1
commit
58cdb73bf8
8 files changed
+654
-108
No files matched your search
@@ -1044,7 +1044,8 @@ void cont_player(void)
|
||||
|
||||
|
||||
/* ================================================================== */
|
||||
/* M2.12 MeowAPPs 包数据目录 + .mpk 接口 */
|
||||
/* M2.12 MeowAPPs 包数据目录 + .mpk 导出接口 */
|
||||
/* 安装入口 meow_pkg_install M42 起移到 pkg_install.c(委托 nyai)。 */
|
||||
/* exFAT 无符号链接 → 数据目录用环境变量(MEOW_DATA_DIR/CACHE_DIR) */
|
||||
/* 传址给应用,不做 symlink(APPS.md 附录 v2 修订)。 */
|
||||
/* ================================================================== */
|
||||
@@ -1127,16 +1128,6 @@ static int pkg_json_field_mpk(const char *mpk, const char *key, char *out, int n
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 安装成功后,把原签 .mpk 另存 MeowData/<pkg>/data/<pkg>.mpk.src(导出保留签名)。 */
|
||||
static void pkg_save_src(const char *mpk, const char *pkg)
|
||||
{
|
||||
char dst[320], cmd[720];
|
||||
snprintf(dst, sizeof dst, "/usr/data/mnt/sd_0/MeowData/%s/data/%s.mpk.src", pkg, pkg);
|
||||
snprintf(cmd, sizeof cmd, "mkdir -p '/usr/data/mnt/sd_0/MeowData/%s/data' 2>/dev/null && "
|
||||
"cp '%s' '%s' 2>/dev/null", pkg, mpk, dst);
|
||||
if (system(cmd) != 0) logmsg("pkg_install: save .mpk.src fail for %s", pkg);
|
||||
}
|
||||
|
||||
/* 导出保留签名:缓存 .mpk.src 存在 + 验签通过 + 其 app.json 版本 == 已装 → 直接复制(不二次签名)。
|
||||
* 成功 0;否则 -1(调用方现场重打包,无签名)。 */
|
||||
static int pkg_export_preserve_sig(const char *pkg, const char *dst)
|
||||
@@ -1157,97 +1148,6 @@ static int pkg_export_preserve_sig(const char *pkg, const char *dst)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* .mpk 安装(本期接口/CLI + 文档;UI 后期):解压 .mpk → MeowAPPs/<pkg>/。
|
||||
* 约定 .mpk = tar.gz,顶层即包目录(含 app.json);包名 = 文件名茎 <pkg>.mpk。
|
||||
* 同名先备份;解压后校验 app.json;随后数据 init(MeowData/<pkg>/{data,cache})。
|
||||
* 返回 0 ok / 负=错误。 */
|
||||
int meow_pkg_install(const char *mpk_path)
|
||||
{
|
||||
if (!mpk_path || access(mpk_path, R_OK) != 0) { logmsg("pkg_install: bad mpk %s", mpk_path?mpk_path:"(null)"); return -1; }
|
||||
if (!sd_device_present()) { logmsg("pkg_install: no card"); return -1; }
|
||||
if (strpbrk(mpk_path, " ;`$'\"&|<>") != NULL) { logmsg("pkg_install: unsafe path"); return -2; }
|
||||
/* 包名 = 文件名茎(去掉 .mpk) */
|
||||
const char *bn = strrchr(mpk_path, '/');
|
||||
bn = bn ? bn + 1 : mpk_path;
|
||||
const char *dot = strrchr(bn, '.');
|
||||
char pkg[64];
|
||||
if (!dot || strcmp(dot, ".mpk") != 0) { logmsg("pkg_install: need <pkg>.mpk"); return -1; }
|
||||
int len = (int)(dot - bn);
|
||||
if (len <= 0 || len >= (int)sizeof pkg) { logmsg("pkg_install: bad pkg name"); return -1; }
|
||||
memcpy(pkg, bn, len); pkg[len] = 0;
|
||||
/* 目标 MeowAPPs/<pkg>/;同名先备份(.bak_<pid>),再解压,再校验 app.json */
|
||||
char dst[256], bak[256], cmd[760];
|
||||
snprintf(dst, sizeof dst, "/usr/data/mnt/sd_0/MeowAPPs/%s", pkg);
|
||||
snprintf(bak, sizeof bak, "%s.bak_%d", dst, (int)getpid());
|
||||
int replacing = (access(dst, F_OK) == 0);
|
||||
|
||||
/* II 期:签名分级先于解包(MPK_SIGNATURE_DESIGN §5.2:先验签再解包,失败不落盘)。
|
||||
* 有签名必须验签(失败拒装);os.meow.* 必须有有效签名;无签名第三方放行(告警)。 */
|
||||
int vr = pkg_verify_sig(mpk_path, pkg);
|
||||
int sysp = (strncmp(pkg, "os.meow.", 8) == 0);
|
||||
if (vr == 1) { logmsg("pkg_install: sig invalid, reject %s", pkg); return -3; }
|
||||
else if (vr == 2) {
|
||||
if (sysp) { logmsg("pkg_install: sys pkg %s must be signed, rejected", pkg); return -3; }
|
||||
logmsg("pkg_install: %s unsigned (3rd party allowed)", pkg);
|
||||
}
|
||||
else if (vr != 0) {
|
||||
if (sysp) { logmsg("pkg_install: meow_verify unavailable rc=%d, sys pkg reject", vr); return -3; }
|
||||
logmsg("pkg_install: meow_verify unavailable rc=%d, %s unverified (3rd party)", vr, pkg);
|
||||
}
|
||||
|
||||
/* 防降级门禁(与 nyai 同规范,APPS.md §3.8):仅按 version_code 比较。
|
||||
* 新版 code < 已装 → 拒;相等 → 允许重装;任一侧缺 version_code → 拒。 */
|
||||
if (replacing) {
|
||||
char nc[32], oc[32];
|
||||
long long ncode = -1, ocode = -1;
|
||||
int nok = 0, ok2 = 0;
|
||||
if (pkg_json_field_mpk(mpk_path, "version_code", nc, sizeof nc) == 0) {
|
||||
char *ep; long long v = strtoll(nc, &ep, 10);
|
||||
if (ep != nc && *ep == '\0') { ncode = v; nok = 1; }
|
||||
}
|
||||
char instj[280];
|
||||
snprintf(instj, sizeof instj, "/usr/data/mnt/sd_0/MeowAPPs/%s/app.json", pkg);
|
||||
if (pkg_json_field(instj, "version_code", oc, sizeof oc) == 0) {
|
||||
char *ep; long long v = strtoll(oc, &ep, 10);
|
||||
if (ep != oc && *ep == '\0') { ocode = v; ok2 = 1; }
|
||||
}
|
||||
if (!nok || !ok2) { logmsg("pkg_install: %s version_code missing, reject", pkg); return -4; }
|
||||
if (ncode < ocode) { logmsg("pkg_install: downgrade %s new %lld < old %lld, reject", pkg, ncode, ocode); return -4; }
|
||||
}
|
||||
|
||||
/* BusyBox tar 无 -z:用 gzip -dc | tar -x 处理 tar.gz。 */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"rm -rf '%s' 2>/dev/null; "
|
||||
"[ -d '%s' ] && mv '%s' '%s' 2>/dev/null; "
|
||||
"mkdir -p '%s' && gzip -dc '%s' | tar -x -C '%s' 2>/dev/null && "
|
||||
"[ -f '%s/app.json' ]", bak, dst, dst, bak, dst, mpk_path, dst, dst);
|
||||
if (system(cmd) != 0) { logmsg("pkg_install: extract/validate fail for %s", pkg); return -1; }
|
||||
|
||||
/* II 期:app.json.package 绑定(== 包名,禁改包名/目录改名;防第三方面具系统包)。 */
|
||||
{
|
||||
char appj[280], ph[64];
|
||||
snprintf(appj, sizeof appj, "/usr/data/mnt/sd_0/MeowAPPs/%s/app.json", pkg);
|
||||
if (pkg_json_field(appj, "package", ph, sizeof ph) == 0 && strcmp(ph, pkg) != 0) {
|
||||
logmsg("pkg_install: app.json.package %s != %s, reject", ph, pkg);
|
||||
char rb[760];
|
||||
snprintf(rb, sizeof rb, "rm -rf '%s' 2>/dev/null; [ -d '%s' ] && mv '%s' '%s' 2>/dev/null",
|
||||
dst, bak, bak, dst);
|
||||
system(rb);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
/* 数据 init:MeowData/<pkg>/{data,cache} */
|
||||
snprintf(cmd, sizeof cmd, "mkdir -p '/usr/data/mnt/sd_0/MeowData/%s/data' "
|
||||
"'/usr/data/mnt/sd_0/MeowData/%s/cache' 2>/dev/null", pkg, pkg);
|
||||
system(cmd);
|
||||
/* 有效签 → 原签 .mpk 另存 MeowData/<pkg>/data/<pkg>.mpk.src(导出保留签名) */
|
||||
if (vr == 0) pkg_save_src(mpk_path, pkg);
|
||||
logmsg("pkg_install: %s installed (old backed up %s)", pkg, bak);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* .mpk 导出:打包 MeowAPPs/<pkg>/(数据目录本就由 env 指向 MeowData,MeowAPPs/<pkg> 下无
|
||||
* data/cache 实体,直接全打包)→ <pkg>.mpk。返回 0 ok。 */
|
||||
int meow_pkg_export(const char *pkg, const char *out_path)
|
||||
|
||||
Reference in new issue
Block a user