diff --git a/patch_v2/meow_launcherd/app_launcher.c b/patch_v2/meow_launcherd/app_launcher.c index 0d0ae05..e2e848f 100644 --- a/patch_v2/meow_launcherd/app_launcher.c +++ b/patch_v2/meow_launcherd/app_launcher.c @@ -620,6 +620,26 @@ int meow_pkg_install(const char *mpk_path) 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; " diff --git a/patch_v2/meow_launcherd/meow_launcherd b/patch_v2/meow_launcherd/meow_launcherd index e1b32b6..d15a02a 100755 Binary files a/patch_v2/meow_launcherd/meow_launcherd and b/patch_v2/meow_launcherd/meow_launcherd differ diff --git a/patch_v2/meowpkg/nyai b/patch_v2/meowpkg/nyai index e9869ca..73cb408 100755 Binary files a/patch_v2/meowpkg/nyai and b/patch_v2/meowpkg/nyai differ diff --git a/patch_v2/meowpkg/nyai.c b/patch_v2/meowpkg/nyai.c index a35e348..11793fd 100644 --- a/patch_v2/meowpkg/nyai.c +++ b/patch_v2/meowpkg/nyai.c @@ -5,7 +5,7 @@ * APPS.md 附录 v2)。支持镜像源: * - 源配置 MeowOS/opt/nyai/sources.list(每行一个源;local <路径> / http / * deb [trusted=yes] / file:// ; # 注释)。默认 local MeowOS/opt/repo。 - * - 每源一个 Packages 索引(行式 Package/Version/Filename/Size/SHA256/Depends/Description), + * - 每源一个 Packages 索引(行式 Package/Version/Version-Code/Filename/Size/SHA256/Depends/Description), * 由 `nyai index <目录>` 生成。 * - update 缓存索引到 MeowOS/opt/nyai/cache;install 按名称从索引取包(本地文件或 http 拉取), * 处理 Depends(递归)、校验 SHA256 + app.json;同 name 先备份;解压 MeowAPPs//; @@ -58,6 +58,8 @@ typedef struct { typedef struct { char pkg[128]; char version[64]; + long long version_code; /* 索引 Version-Code(正整数;has_=0 表示索引缺该项) */ + int has_version_code; char filename[256]; char sha256[128]; char depends[256]; @@ -154,67 +156,6 @@ static void human_size(long long b, char *out, int n) else snprintf(out,n,"%.2f MB", b/(1024.0*1024)); } -/* 简版版本比较(点/横分段,数字段按数值):a>b 返回 1,=0,< -1 */ -static int version_cmp(const char *a, const char *b) -{ - const char *x=a,*y=b; - while (*x || *y) { - while (*x=='.'||*x=='-') x++; - while (*y=='.'||*y=='-') y++; - if (!*x || !*y) break; - if (isdigit((unsigned char)*x) && isdigit((unsigned char)*y)) { - long nx=0, ny=0; - while (isdigit((unsigned char)*x)) nx=nx*10+(*x++ - '0'); - while (isdigit((unsigned char)*y)) ny=ny*10+(*y++ - '0'); - if (nx!=ny) return nx 0x7fffffffUL) return -1; } - if (n < max) segs[n++] = x; - } - return n ? n : -1; -} -static int version_ok(const char *v) -{ unsigned long s[MAXVSEG]; return version_parse(v, s, MAXVSEG) >= 0; } -/* 返回:a>b=1, a==b=0, a cb ? ca : cb; - for (int i = 0; i < n; i++) { - unsigned long xa = i < ca ? na[i] : 0UL; /* 段数不齐,短者补 0 */ - unsigned long xb = i < cb ? nb[i] : 0UL; - if (xa != xb) return xa < xb ? -1 : 1; - } - return 0; -} - /* ---------------- JSON 读取 ---------------- */ static int json_str(const char *j, const char *key, char *out, int n) { @@ -278,10 +219,38 @@ static int json_raw(const char *j, const char *key, char *out, int n) out[o]=0; return out[0] ? 0 : -1; } +/* 读 JSON 整型字段(键带引号、值必须为纯数字,如 "version_code": 12)。 + * 缺键 / 值空 / 非数字 / 溢出 → 返回 -1;成功返回 0 并写入 *out。 */ +static int json_int(const char *j, const char *key, long long *out) +{ + if (!j || !key || !out) return -1; + char pat[160]; + snprintf(pat, sizeof pat, "\"%s\"", key); + const char *p = j; + while ((p = strstr(p, pat)) != NULL) { + p += strlen(pat); + while (*p==' '||*p=='\t'||*p=='\n'||*p=='\r') p++; + if (*p != ':') { p++; continue; } + p++; + while (*p==' '||*p=='\t'||*p=='\n'||*p=='\r') p++; + if (!isdigit((unsigned char)*p)) return -1; /* 缺值 / 字符串 / 非数字 */ + long long v = 0; + while (isdigit((unsigned char)*p)) { + int d = *p++ - '0'; + if (v > (LLONG_MAX - d) / 10) return -1; /* 溢出 */ + v = v * 10 + d; + } + *out = v; + return 0; + } + return -1; +} typedef struct { char name[256]; - char version[64]; + char version[64]; /* 仅作显示字符串(versionName 角色,不作比较) */ + long long version_code; /* app.json "version_code"(正整数,安装/升级比较唯一依据;缺时 has_=0) */ + int has_version_code; /* app.json 是否显式声明 version_code */ char exec[512]; char package[64]; /* app.json "package"(权威包名,== 文件名茎;II 期绑定校验用) */ char author[128]; @@ -309,6 +278,7 @@ static int load_app_meta(const char *path, appmeta *m) if (json_str(j,"exec",m->exec,sizeof m->exec)!=0 || !m->exec[0]) return -1; if (json_str(j,"package",m->package,sizeof m->package)!=0) m->package[0]=0; if (json_str(j,"version",m->version,sizeof m->version)!=0) m->version[0]=0; + m->has_version_code = (json_int(j,"version_code",&m->version_code)==0); if (json_str(j,"author",m->author,sizeof m->author)!=0) m->author[0]=0; if (json_str(j,"depends",m->depends,sizeof m->depends)!=0) m->depends[0]=0; if (json_str(j,"description",m->description,sizeof m->description)!=0) m->description[0]=0; @@ -344,6 +314,7 @@ static int mpk_appinfo(const char *file, appmeta *m) j[r]=0; if (json_str(j,"name",m->name,sizeof m->name)!=0 || !m->name[0]) return -1; if (json_str(j,"version",m->version,sizeof m->version)!=0) m->version[0]=0; + m->has_version_code = (json_int(j,"version_code",&m->version_code)==0); if (json_str(j,"depends",m->depends,sizeof m->depends)!=0) m->depends[0]=0; if (json_str(j,"description",m->description,sizeof m->description)!=0) m->description[0]=0; return 0; @@ -490,6 +461,7 @@ static int parse_index(const char *path, pkgrec *recs, int max) char *key = trim(t), *val = trim(colon+1); if (!strcmp(key,"Package")) { snprintf(cur.pkg,sizeof cur.pkg,"%s",val); have = (cur.pkg[0]!=0); } else if (!strcmp(key,"Version")) snprintf(cur.version,sizeof cur.version,"%s",val); + else if (!strcmp(key,"Version-Code")) { cur.version_code = atoll(val); cur.has_version_code = (val[0]!=0); } else if (!strcmp(key,"Filename")) snprintf(cur.filename,sizeof cur.filename,"%s",val); else if (!strcmp(key,"SHA256")) snprintf(cur.sha256,sizeof cur.sha256,"%s",val); else if (!strcmp(key,"Size")) { cur.size = atoll(val); } @@ -684,6 +656,7 @@ static int cmd_index(const char *dir) char sha[128]=""; sha256_file(fpath, sha, sizeof sha); fprintf(f,"Package: %s\n", stem); fprintf(f,"Version: %s\n", ver[0]?ver:"0.0.0"); + if (m.has_version_code) fprintf(f,"Version-Code: %lld\n", m.version_code); fprintf(f,"Filename: %s\n", de->d_name); fprintf(f,"Size: %lld\n", (long long)st.st_size); if (sha[0]) fprintf(f,"SHA256: %s\n", sha); @@ -738,7 +711,9 @@ static int cmd_list_upgradable(pkgrec *recs, int nrec) char ap[BUF]; snprintf(ap,sizeof ap,"%s/%s/app.json", r, de->d_name); appmeta m; if (load_app_meta(ap,&m)!=0) break; - if (version_cmp(recs[i].version, m.version) > 0) { + /* 仅按 version_code 比较(APPS.md §3.8);任一侧缺 code → 不判定为可升级 */ + if (recs[i].has_version_code && m.has_version_code && + recs[i].version_code > m.version_code) { printf("%-24s %-12s %-12s\n", de->d_name, m.version[0]?m.version:"?", recs[i].version); n++; } @@ -906,17 +881,18 @@ static int install_mpk_file(const char *mpk_path) rm_rf(stg); return 1; } - /* ② 同包名替换前的防降级门禁:新版必须 >= 已装(相等=重装,允许;<即降级拒装) */ + /* ② 同包名替换前的防降级门禁(APPS.md §3.8,仅按 version_code 比较): + * 新版 code >= 已装 code(相等=重装,允许;<即降级拒装);任一侧缺 version_code → 拒装。 */ if (replacing) { char instj[BUF]; snprintf(instj,sizeof instj,"%s/app.json", dst); appmeta im; - if (load_app_meta(instj,&im)!=0 || !version_ok(im.version) || !version_ok(m.version)) { - fprintf(stderr,"nyai: install: 版本无法判定(新=「%s」 已装=「%s」),拒绝安装\n", - m.version[0]?m.version:"(缺失)", im.version[0]?im.version:"(缺失)"); + if (load_app_meta(instj,&im)!=0 || !m.has_version_code || !im.has_version_code) { + fprintf(stderr,"nyai: install: 版本号无法判定(新缺 version_code 或已装缺 version_code),拒绝安装\n"); rm_rf(stg); return 1; } - if (version_num_cmp(m.version, im.version) < 0) { - fprintf(stderr,"nyai: install: 不允许降级: 新版本 %s < 已装 %s\n", m.version, im.version); + if (m.version_code < im.version_code) { + fprintf(stderr,"nyai: install: 不允许降级: 新版本 code %lld < 已装 code %lld\n", + m.version_code, im.version_code); rm_rf(stg); return 1; } rm_rf(bak); @@ -1034,6 +1010,8 @@ static int cmd_info(const char *pkg) printf("Package: %s\n", pkg); printf("Name: %s\n", m.name); printf("Version: %s\n", m.version[0]?m.version:"(none)"); + if (m.has_version_code) printf("Version-Code: %lld\n", m.version_code); + else printf("Version-Code: (none)\n"); printf("Author: %s\n", m.author[0]?m.author:"(none)"); printf("Exec: %s\n", m.exec); printf("Size: %s\n", hs); @@ -1247,18 +1225,19 @@ static int cmd_upgrade(const char *pkg, const char *file) else if (vr == 2) { if (sysp) { fprintf(stderr,"nyai: upgrade: 系统包 %s 必须签名,拒绝升级\n", pkg); return 1; } printf("nyai: 提示: %s 未签名(第三方放行)\n", pkg); } else if (vr != 0) { if (sysp) { fprintf(stderr,"nyai: upgrade: meow_verify 不可用(rc=%d),os.meow.* 拒升\n", vr); return 1; } printf("nyai: 警告: meow_verify 不可用(rc=%d),%s 未验签放行\n", vr, pkg); } } - /* 防降级门禁(与 install 同规范):新版必须 >= 已装(相等=重装允许) */ + /* 防降级门禁(与 install 同规范,APPS.md §3.8,仅按 version_code 比较): + * 新版 code >= 已装 code(相等=重装允许);任一侧缺 version_code → 拒绝。 */ { char aj[BUF]; snprintf(aj,sizeof aj,"%s/app.json", dst); appmeta im, nm; if (load_app_meta(aj,&im)!=0 || mpk_appinfo(mpk,&nm)!=0 || - !version_ok(im.version) || !version_ok(nm.version)) { - fprintf(stderr,"nyai: upgrade: 版本无法判定(新=「%s」 已装=「%s」),拒绝\n", - nm.version[0]?nm.version:"(缺失)", im.version[0]?im.version:"(缺失)"); + !im.has_version_code || !nm.has_version_code) { + fprintf(stderr,"nyai: upgrade: 版本号无法判定(新缺 version_code 或已装缺 version_code),拒绝\n"); return 1; } - if (version_num_cmp(nm.version, im.version) < 0) { - fprintf(stderr,"nyai: upgrade: 不允许降级: 新版本 %s < 已装 %s\n", nm.version, im.version); + if (nm.version_code < im.version_code) { + fprintf(stderr,"nyai: upgrade: 不允许降级: 新版本 code %lld < 已装 code %lld\n", + nm.version_code, im.version_code); return 1; } } diff --git a/patch_v2/meowpkg/nyai_host b/patch_v2/meowpkg/nyai_host index 67ba967..7620043 100755 Binary files a/patch_v2/meowpkg/nyai_host and b/patch_v2/meowpkg/nyai_host differ