已开启
appspawn: PATH环境变量调整优先级及弱沙箱应用保持原有顺序 #2947
appspawn: PATH环境变量调整优先级及弱沙箱应用保持原有顺序 #2947
已开启
soulrequiem创建于 3 天前
8 个文件变更+247-14
@@ -88,7 +88,7 @@ hnp安装包路径
88| 公有 | `/data/app/el1/bundle/<uid>/hnppublic/<name>.org/<name>_<ver>` | `/data/service/hnp/bin` | `HNP_PUBLIC_HOME=/data/service/hnp` |88| 公有 | `/data/app/el1/bundle/<uid>/hnppublic/<name>.org/<name>_<ver>` | `/data/service/hnp/bin` | `HNP_PUBLIC_HOME=/data/service/hnp` |
89| 私有 | `/data/app/el1/bundle/<uid>/hnp/<hapPkg>/<name>.org/<name>_<ver>` | `/data/app/<bundleName>/...` | `HNP_PRIVATE_HOME=/data/app` |89| 私有 | `/data/app/el1/bundle/<uid>/hnp/<hapPkg>/<name>.org/<name>_<ver>` | `/data/app/<bundleName>/...` | `HNP_PRIVATE_HOME=/data/app` |
90 90 
91-- `HNP_PRIVATE_HOME` 排在 `HNP_PUBLIC_HOME` **之前**,同名二进制优先执行私有路径下的。91+- `HNP_PRIVATE_HOME` 排在 `HNP_PUBLIC_HOME` **之前**,且系统 PATH 居中,优先级为:私有 hnp > 系统自带命令 > 公有 hnp。同名二进制优先执行私有路径下的,其次为系统自带命令,最后为公有 hnp 路径下的
92- 公有 hnp 可被所有应用访问;私有 hnp 仅安装它的 HAP 可访问。92- 公有 hnp 可被所有应用访问;私有 hnp 仅安装它的 HAP 可访问。
93- 公有 hnp 安装信息记录在 `hnp_info_<uid>.json`(旧路径 `hnp_info.json`),私有 hnp 不记入此文件。93- 公有 hnp 安装信息记录在 `hnp_info_<uid>.json`(旧路径 `hnp_info.json`),私有 hnp 不记入此文件。
94 94 
@@ -402,11 +402,11 @@ appspawn 为每个应用进程注入环境变量(`util/src/appspawn_utils.c:40
402```c402```c
403{"HNP_PRIVATE_HOME", "/data/app", false},403{"HNP_PRIVATE_HOME", "/data/app", false},
404{"HNP_PUBLIC_HOME", "/data/service/hnp", false},404{"HNP_PUBLIC_HOME", "/data/service/hnp", false},
405-{"PATH", "${HNP_PRIVATE_HOME}/bin:${HNP_PUBLIC_HOME}/bin:${PATH}", false},405+{"PATH", "${HNP_PRIVATE_HOME}/bin:${PATH}:${HNP_PUBLIC_HOME}/bin", false},
406```406```
407 407 
408- `HNP_PRIVATE_HOME``HNP_PUBLIC_HOME` 指向沙箱内挂载点(与 11.2 的 sandbox-path 对应)。408- `HNP_PRIVATE_HOME``HNP_PUBLIC_HOME` 指向沙箱内挂载点(与 11.2 的 sandbox-path 对应)。
409-- `PATH` 中 `HNP_PRIVATE_HOME/bin` 排在 `HNP_PUBLIC_HOME/bin` **前面**,同名二进制优先执行私有路径下的409+- `PATH` 中 `HNP_PRIVATE_HOME/bin` 排在 `${PATH}` **前面**,系统 PATH 居中,`HNP_PUBLIC_HOME/bin` 排在最后。优先级为:私有 hnp > 系统自带命令 > 公有 hnp弱沙箱应用(custom_sandbox)通过 `SetLegacyHnpPathOrder` 恢复原有顺序(私有 hnp > 公有 hnp > 系统)。
410- 环境变量中的 `${HNP_PRIVATE_HOME}` 等占位符在注入前由 `ConvertEnvValue``appspawn_utils.c:50`)展开。410- 环境变量中的 `${HNP_PRIVATE_HOME}` 等占位符在注入前由 `ConvertEnvValue``appspawn_utils.c:50`)展开。
411 411 
412### 11.5 运行时视角412### 11.5 运行时视角
@@ -128,6 +128,19 @@ APPSPAWN_STATIC int32_t LoadCustomSandboxEnv(const char* envFilePath)
128 return APPSPAWN_OK;128 return APPSPAWN_OK;
129}129}
130 130 
131+// Restore legacy HNP PATH order before SpawnSetAppEnv (AppEnv JSON) so that
132+// app-declared env in AppEnv JSON and /etc/environment can still override it.
133+APPSPAWN_STATIC int SpawnSetLegacyHnpPath(AppSpawnMgr *content, AppSpawningCtx *property)
134+{
135+ APPSPAWN_LOGV("Spawning: set SpawnSetLegacyHnpPath.");
136+ if (CheckAppMsgFlagsSet(property, APP_FLAGS_CUSTOM_SANDBOX)) {
137+ // custom sandbox (weak sandbox) apps keep legacy PATH order:
138+ // HNP_PRIVATE > HNP_PUBLIC > system
139+ SetLegacyHnpPathOrder();
140+ }
141+ return APPSPAWN_OK;
142+}
143+ 
131APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property)144APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property)
132{145{
133 APPSPAWN_LOGV("Spawning: set SpawnSetCustomSandboxEnv.");146 APPSPAWN_LOGV("Spawning: set SpawnSetCustomSandboxEnv.");
@@ -221,6 +234,8 @@ int GetUserId(uint32_t *userId)
221 234 
222MODULE_CONSTRUCTOR(void)235MODULE_CONSTRUCTOR(void)
223{236{
224- const int32_t priority = HOOK_PRIO_COMMON + 2; // after SpawnSetAppEnv237+ // legacy PATH order runs before SpawnSetAppEnv (HOOK_PRIO_COMMON + 1)
225- AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, priority, SpawnSetCustomSandboxEnv);238+ // so AppEnv JSON and /etc/environment can override it
239+ AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_HIGHEST + 1, SpawnSetLegacyHnpPath);
240+ AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_COMMON + 2, SpawnSetCustomSandboxEnv);
226}241}
@@ -57,7 +57,7 @@ hnp目录准备完成后,参考第4步在hap打包命令中用--hnp-path指定
57**规格:**57**规格:**
58 58 
591. 公有hnp包安装后应用进程沙箱路径为/data/service/hnp/xxx.org/xxx_yyy,私有hnp包安装后应用进程沙箱路径为/data/app/<bundleName>/xxx.org/xxx_yyy,其中xxx值hnp包hnp.json文件"name"字段,yyy值为hnp包中"version"字段。591. 公有hnp包安装后应用进程沙箱路径为/data/service/hnp/xxx.org/xxx_yyy,私有hnp包安装后应用进程沙箱路径为/data/app/<bundleName>/xxx.org/xxx_yyy,其中xxx值hnp包hnp.json文件"name"字段,yyy值为hnp包中"version"字段。
60-2. 公有hnp包根路径的环境变量HNP_PUBLIC_HOME=/data/service/hnp,私有hnp包根路径的环境变量HNP_PRIVATE_HOME=/data/app。HNP_PRIVATE_HOME环境变量排序HNP_PUBLIC_HOME前面,意味着如果存在同名二进制分别在有hnp路径下和有hnp路径下,优先执行私有hnp路径下二进制。60+2. 公有hnp包根路径的环境变量HNP_PUBLIC_HOME=/data/service/hnp,私有hnp包根路径的环境变量HNP_PRIVATE_HOME=/data/app。PATH环境变量排序为HNP_PRIVATE_HOME/bin:${PATH}:HNP_PUBLIC_HOME/bin即私有hnp路径优先级最高,其次为系统自带命令路径,最后为公有hnp路径。意味着如果存在同名二进制分别在有hnp路径下、系统路径下有hnp路径下,优先执行私有hnp路径下二进制,其次为系统自带命令,最后为公有hnp路径下二进制
613. 公有hnp包可以被所有应用访问,私有hnp包只允许被安装该hnp包的hap应用访问。613. 公有hnp包可以被所有应用访问,私有hnp包只允许被安装该hnp包的hap应用访问。
624. 卸载hap应用会同步卸载该hap应用安装的所有hnp包,如果该hnp包中二进制正在被其他应用使用,则会导致hap应用卸载失败。624. 卸载hap应用会同步卸载该hap应用安装的所有hnp包,如果该hnp包中二进制正在被其他应用使用,则会导致hap应用卸载失败。
635. Hap应用A和B安装先后安装同名公有hnp包(hnp.json文件中"name")。后安装的应用B会无法安装,需要卸载hap应用A或者将应用B中的公有hnp修改为私有hnp方可继续安装。635. Hap应用A和B安装先后安装同名公有hnp包(hnp.json文件中"name")。后安装的应用B会无法安装,需要卸载hap应用A或者将应用B中的公有hnp修改为私有hnp方可继续安装。
@@ -108,8 +108,8 @@ int main(int argc, char *const argv[])
108 if (end == 0) {108 if (end == 0) {
109 return 0;109 return 0;
110 }110 }
111- InitCommonEnv();
112 CheckPreload(argv);111 CheckPreload(argv);
112+ InitCommonEnv();
113 (void)signal(SIGPIPE, SIG_IGN);113 (void)signal(SIGPIPE, SIG_IGN);
114 uint32_t argvSize = end - start;114 uint32_t argvSize = end - start;
115 AppSpawnStartArg *arg;115 AppSpawnStartArg *arg;
@@ -36,6 +36,7 @@ APPSPAWN_STATIC std::string_view TrimWhitespaceView(std::string_view str);
36APPSPAWN_STATIC int32_t ParseEnvLine(const std::string &line, std::string &envName, std::string &envValue);36APPSPAWN_STATIC int32_t ParseEnvLine(const std::string &line, std::string &envName, std::string &envValue);
37APPSPAWN_STATIC int32_t LoadCustomSandboxEnv(const char* envFilePath);37APPSPAWN_STATIC int32_t LoadCustomSandboxEnv(const char* envFilePath);
38APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property);38APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property);
39+APPSPAWN_STATIC int SpawnSetLegacyHnpPath(AppSpawnMgr *content, AppSpawningCtx *property);
39APPSPAWN_STATIC int SetUidGid(const AppSpawnMgr *content, const AppSpawningCtx *property);40APPSPAWN_STATIC int SetUidGid(const AppSpawnMgr *content, const AppSpawningCtx *property);
40#ifdef __cplusplus41#ifdef __cplusplus
41extern "C" {42extern "C" {
@@ -105,9 +106,67 @@ HWTEST_F(AppSpawnCustomConfigTest, SpawnSetCustomSandboxEnv_01, TestSize.Level0)
105 ASSERT_EQ(ret, 0);106 ASSERT_EQ(ret, 0);
106}107}
107 108 
109+/**
110+ * @brief SpawnSetLegacyHnpPath_01 - custom_sandbox flag set, triggers SetLegacyHnpPathOrder
111+ */
112+HWTEST_F(AppSpawnCustomConfigTest, SpawnSetLegacyHnpPath_01, TestSize.Level0)
113+{
114+ int ret = -1;
115+ AppSpawnClientHandle clientHandle = nullptr;
116+ AppSpawningCtx *property = nullptr;
117+ do {
118+ ret = AppSpawnClientInit(APPSPAWN_SERVER_NAME, &clientHandle);
119+ APPSPAWN_CHECK(ret == 0, break, "Failed to create client %{public}s", APPSPAWN_SERVER_NAME);
120+ AppSpawnReqMsgHandle reqHandle = g_testHelper.CreateMsg(clientHandle, MSG_APP_SPAWN, 0);
121+ APPSPAWN_CHECK(reqHandle != nullptr, break, "Failed to create msg");
122+ AppSpawnReqMsgSetAppFlag(reqHandle, APP_FLAGS_CUSTOM_SANDBOX);
123+ property = g_testHelper.GetAppProperty(clientHandle, reqHandle);
124+ APPSPAWN_CHECK(property != nullptr, break, "Failed to get app property");
125+ 
126+ InitCommonEnv();
127+ setenv("PATH", "/data/app/bin:/system/bin:/data/service/hnp/bin", 1);
128+ ret = SpawnSetLegacyHnpPath(nullptr, property);
129+ ASSERT_EQ(ret, 0);
130+ const char *env = getenv("PATH");
131+ ASSERT_NE(env, nullptr);
132+ EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin:/system/bin");
133+ } while (0);
134+ DeleteAppSpawningCtx(property);
135+ AppSpawnClientDestroy(clientHandle);
136+ ASSERT_EQ(ret, 0);
137+}
138+ 
139+/**
140+ * @brief SpawnSetLegacyHnpPath_02 - no custom_sandbox flag, skip reorder
141+ */
142+HWTEST_F(AppSpawnCustomConfigTest, SpawnSetLegacyHnpPath_02, TestSize.Level0)
143+{
144+ int ret = -1;
145+ AppSpawnClientHandle clientHandle = nullptr;
146+ AppSpawningCtx *property = nullptr;
147+ do {
148+ ret = AppSpawnClientInit(APPSPAWN_SERVER_NAME, &clientHandle);
149+ APPSPAWN_CHECK(ret == 0, break, "Failed to create client %{public}s", APPSPAWN_SERVER_NAME);
150+ AppSpawnReqMsgHandle reqHandle = g_testHelper.CreateMsg(clientHandle, MSG_APP_SPAWN, 0);
151+ APPSPAWN_CHECK(reqHandle != nullptr, break, "Failed to create msg");
152+ // no APP_FLAGS_CUSTOM_SANDBOX set
153+ property = g_testHelper.GetAppProperty(clientHandle, reqHandle);
154+ APPSPAWN_CHECK(property != nullptr, break, "Failed to get app property");
155+ 
156+ setenv("PATH", "/data/app/bin:/system/bin:/data/service/hnp/bin", 1);
157+ ret = SpawnSetLegacyHnpPath(nullptr, property);
158+ ASSERT_EQ(ret, 0);
159+ const char *env = getenv("PATH");
160+ ASSERT_NE(env, nullptr);
161+ EXPECT_STREQ(env, "/data/app/bin:/system/bin:/data/service/hnp/bin");
162+ } while (0);
163+ DeleteAppSpawningCtx(property);
164+ AppSpawnClientDestroy(clientHandle);
165+ ASSERT_EQ(ret, 0);
166+}
167+ 
108/**168/**
109 * @brief App_Spawn_TrimWhitespace_01169 * @brief App_Spawn_TrimWhitespace_01
110- *
111 */170 */
112HWTEST_F(AppSpawnCustomConfigTest, App_Spawn_TrimWhitespace_01, TestSize.Level0)171HWTEST_F(AppSpawnCustomConfigTest, App_Spawn_TrimWhitespace_01, TestSize.Level0)
113{172{
@@ -1110,10 +1110,15 @@ HWTEST_F(AppSpawnServiceTest, App_Spawn_InitCommonEnv_001, TestSize.Level0)
1110 EXPECT_EQ(strcmp(env, "/data/service/hnp"), 0);1110 EXPECT_EQ(strcmp(env, "/data/service/hnp"), 0);
1111 }1111 }
1112 env = getenv("PATH");1112 env = getenv("PATH");
1113- EXPECT_NE(env, nullptr);1113+ ASSERT_NE(env, nullptr);
1114- if (env != nullptr) {1114+ // default order: HNP_PRIVATE > system > HNP_PUBLIC
1115- EXPECT_NE(strstr(env, "/data/app/bin:/data/service/hnp/bin"), nullptr);1115+ EXPECT_NE(strstr(env, "/data/app/bin:"), nullptr);
1116- }1116+ EXPECT_NE(strstr(env, ":/data/service/hnp/bin"), nullptr);
1117+ const char *publicPos = strstr(env, "/data/service/hnp/bin");
1118+ const char *privatePos = strstr(env, "/data/app/bin");
1119+ ASSERT_NE(publicPos, nullptr);
1120+ ASSERT_NE(privatePos, nullptr);
1121+ EXPECT_TRUE(publicPos > privatePos);
1117 }1122 }
1118 env = getenv("HOME");1123 env = getenv("HOME");
1119 EXPECT_NE(env, nullptr);1124 EXPECT_NE(env, nullptr);
@@ -1137,6 +1142,110 @@ HWTEST_F(AppSpawnServiceTest, App_Spawn_InitCommonEnv_001, TestSize.Level0)
1137 }1142 }
1138}1143}
1139 1144 
1145+/**
1146+ * @brief SetLegacyHnpPathOrder: HNP segments in middle/tail positions
1147+ */
1148+HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_MiddleTail_001, TestSize.Level0)
1149+{
1150+ // PATH with hnp segments in middle and tail: system:hnp_private:system:hnp_public
1151+ setenv("PATH", "/system/bin:/data/app/bin:/vendor/bin:/data/service/hnp/bin", 1);
1152+ SetLegacyHnpPathOrder();
1153+ const char *env = getenv("PATH");
1154+ ASSERT_NE(env, nullptr);
1155+ // legacy order: hnp_private:hnp_public:system(remainder in original order)
1156+ EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin:/system/bin:/vendor/bin");
1157+}
1158+ 
1159+/**
1160+ * @brief SetLegacyHnpPathOrder: only hnp segments, no system path
1161+ */
1162+HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_HnpOnly_001, TestSize.Level0)
1163+{
1164+ // PATH with only hnp segments, no system path -> skip reorder
1165+ setenv("PATH", "/data/app/bin:/data/service/hnp/bin", 1);
1166+ SetLegacyHnpPathOrder();
1167+ const char *env = getenv("PATH");
1168+ ASSERT_NE(env, nullptr);
1169+ // unchanged: no system segment to reorder
1170+ EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin");
1171+}
1172+ 
1173+/**
1174+ * @brief SetLegacyHnpPathOrder: PATH is empty string
1175+ */
1176+HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_EmptyPath_001, TestSize.Level0)
1177+{
1178+ setenv("PATH", "", 1);
1179+ SetLegacyHnpPathOrder();
1180+ const char *env = getenv("PATH");
1181+ ASSERT_NE(env, nullptr);
1182+ // empty PATH: no reorder, unchanged
1183+ EXPECT_STREQ(env, "");
1184+}
1185+ 
1186+/**
1187+ * @brief SetLegacyHnpPathOrder: PATH with duplicate hnp segments
1188+ */
1189+HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_DuplicateHnp_001, TestSize.Level0)
1190+{
1191+ // duplicate hnp segments should be deduplicated in legacy output
1192+ setenv("PATH", "/data/app/bin:/system/bin:/data/app/bin:/data/service/hnp/bin:/vendor/bin", 1);
1193+ SetLegacyHnpPathOrder();
1194+ const char *env = getenv("PATH");
1195+ ASSERT_NE(env, nullptr);
1196+ // hnp duplicates dropped, system remainder preserved in order
1197+ EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin:/system/bin:/vendor/bin");
1198+}
1199+ 
1200+/**
1201+ * @brief SetLegacyHnpPathOrder: PATH with only one hnp segment (missing public)
1202+ */
1203+HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_OneHnpOnly_001, TestSize.Level0)
1204+{
1205+ // only private hnp, no public hnp -> skip reorder
1206+ setenv("PATH", "/data/app/bin:/system/bin:/vendor/bin", 1);
1207+ SetLegacyHnpPathOrder();
1208+ const char *env = getenv("PATH");
1209+ ASSERT_NE(env, nullptr);
1210+ // unchanged: both hnp segments must exist to trigger reorder
1211+ EXPECT_STREQ(env, "/data/app/bin:/system/bin:/vendor/bin");
1212+}
1213+ 
1214+/**
1215+ * @brief SetLegacyHnpPathOrder: system path overflow (sysSeg exceeds MAX_ENV_VALUE_LEN)
1216+ */
1217+HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_Overflow_001, TestSize.Level0)
1218+{
1219+ // build a PATH with hnp segments + very long system paths to overflow sysSeg
1220+ std::string longPath = "/data/app/bin:";
1221+ // each segment ~200 chars, need > 5 segments to overflow MAX_ENV_VALUE_LEN(1024)
1222+ // after dropping hnp segments, sysSeg must still exceed 1024
1223+ std::string longSeg(200, 'x');
1224+ for (int i = 0; i < 8; i++) {
1225+ longPath += "/" + longSeg + std::to_string(i) + ":";
1226+ }
1227+ longPath += "/data/service/hnp/bin";
1228+ setenv("PATH", longPath.c_str(), 1);
1229+ SetLegacyHnpPathOrder();
1230+ const char *env = getenv("PATH");
1231+ ASSERT_NE(env, nullptr);
1232+ // overflow: keep original PATH unchanged
1233+ EXPECT_STREQ(env, longPath.c_str());
1234+}
1235+ 
1236+/**
1237+ * @brief SetLegacyHnpPathOrder: hnp segments at head (default InitCommonEnv order)
1238+ */
1239+HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_HeadPosition_001, TestSize.Level0)
1240+{
1241+ // default InitCommonEnv order: private:system:public
1242+ setenv("PATH", "/data/app/bin:/system/bin:/vendor/bin:/data/service/hnp/bin", 1);
1243+ SetLegacyHnpPathOrder();
1244+ const char *env = getenv("PATH");
1245+ ASSERT_NE(env, nullptr);
1246+ EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin:/system/bin:/vendor/bin");
1247+}
1248+ 
1140/**1249/**
1141 * @brief 测试环境变量转换功能是否正常1250 * @brief 测试环境变量转换功能是否正常
1142 *1251 *
@@ -163,6 +163,7 @@ void DumpCurrentDir(char *buffer, uint32_t bufferLen, const char *dirPath);
163int CheckEnabled(const char *param, const char *value);163int CheckEnabled(const char *param, const char *value);
164int IsDeveloperModeOpen();164int IsDeveloperModeOpen();
165void InitCommonEnv(void);165void InitCommonEnv(void);
166+void SetLegacyHnpPathOrder(void);
166int ConvertEnvValue(const char *srcEnv, char *dstEnv, int len);167int ConvertEnvValue(const char *srcEnv, char *dstEnv, int len);
167void SetNoShareFsEnable(bool enable);168void SetNoShareFsEnable(bool enable);
168bool IsNoShareFsEnable(void);169bool IsNoShareFsEnable(void);
@@ -40,7 +40,7 @@
40static const AppSpawnCommonEnv COMMON_ENV[] = {40static const AppSpawnCommonEnv COMMON_ENV[] = {
41 {"HNP_PRIVATE_HOME", "/data/app", false},41 {"HNP_PRIVATE_HOME", "/data/app", false},
42 {"HNP_PUBLIC_HOME", "/data/service/hnp", false},42 {"HNP_PUBLIC_HOME", "/data/service/hnp", false},
43- {"PATH", "${HNP_PRIVATE_HOME}/bin:${HNP_PUBLIC_HOME}/bin:${PATH}", false},43+ {"PATH", "${HNP_PRIVATE_HOME}/bin:${PATH}:${HNP_PUBLIC_HOME}/bin", false},
44 {"HOME", "/storage/Users/currentUser", false},44 {"HOME", "/storage/Users/currentUser", false},
45 {"TMPDIR", "/data/storage/el2/base/cache", false},45 {"TMPDIR", "/data/storage/el2/base/cache", false},
46 {"SHELL", "/bin/sh", false},46 {"SHELL", "/bin/sh", false},
@@ -117,10 +117,59 @@ void InitCommonEnv(void)
117 COMMON_ENV[i].envName, COMMON_ENV[i].envValue);117 COMMON_ENV[i].envName, COMMON_ENV[i].envValue);
118 ret = setenv(COMMON_ENV[i].envName, envValue, true);118 ret = setenv(COMMON_ENV[i].envName, envValue, true);
119 APPSPAWN_CHECK(ret == 0, return, "Set env fail name=%{public}s, value=%{public}s",119 APPSPAWN_CHECK(ret == 0, return, "Set env fail name=%{public}s, value=%{public}s",
120- COMMON_ENV[i].envName, envValue);120+ COMMON_ENV[i].envName, COMMON_ENV[i].envValue);
121 }121 }
122}122}
123 123 
124+// Collect non-hnp segments from path into sysSeg, return false on overflow
125+static bool CollectSysSeg(const char *path, const char *privateSeg, const char *publicSeg,
126+ char *sysSeg, uint32_t sysSegLen)
127+{
128+ char *tmpPath = strdup(path);
129+ APPSPAWN_CHECK(tmpPath != NULL, return false, "Failed to strdup path");
130+ bool collectSuccess = true;
131+ char *savePtr = NULL;
132+ for (char *seg = strtok_r(tmpPath, ":", &savePtr); seg != NULL;
133+ seg = strtok_r(NULL, ":", &savePtr)) {
134+ APPSPAWN_ONLY_EXPER(strcmp(seg, privateSeg) == 0 || strcmp(seg, publicSeg) == 0, continue);
135+ if (sysSeg[0] != '\0') {
136+ errno_t rc = strcat_s(sysSeg, sysSegLen, ":");
137+ APPSPAWN_CHECK(rc == EOK, collectSuccess = false; break, "Failed to append colon");
138+ }
139+ errno_t rc = strcat_s(sysSeg, sysSegLen, seg);
140+ APPSPAWN_CHECK(rc == EOK, collectSuccess = false; break, "Failed to append seg");
141+ }
142+ free(tmpPath);
143+ return collectSuccess;
144+}
145+ 
146+// Reorder current PATH (HNP_PRIVATE:system:HNP_PUBLIC) back to legacy order
147+// (HNP_PRIVATE:HNP_PUBLIC:system) for custom sandbox apps.
148+void SetLegacyHnpPathOrder(void)
149+{
150+ static const char *privateSeg = "/data/app/bin";
151+ static const char *publicSeg = "/data/service/hnp/bin";
152+ const char *path = getenv("PATH");
153+ APPSPAWN_CHECK(path != NULL && path[0] != '\0', return, "Invalid env for legacy path order");
154+ 
155+ APPSPAWN_CHECK(strstr(path, privateSeg) != NULL && strstr(path, publicSeg) != NULL, return,
156+ "PATH has no hnp segments, keep current order");
157+ 
158+ char sysSeg[MAX_ENV_VALUE_LEN] = {0};
159+ APPSPAWN_CHECK(CollectSysSeg(path, privateSeg, publicSeg, sysSeg, sizeof(sysSeg)), return,
160+ "system path overflow, keep current order");
161+ // no system segment means PATH is already hnp-only, no reorder needed
162+ APPSPAWN_CHECK(sysSeg[0] != '\0', return, "PATH has only hnp segments, keep current order");
163+ 
164+ char newPath[MAX_ENV_VALUE_LEN] = {0};
165+ int len = snprintf_s(newPath, sizeof(newPath), sizeof(newPath) - 1,
166+ "%s:%s:%s", privateSeg, publicSeg, sysSeg);
167+ APPSPAWN_CHECK(len > 0, return, "Failed to build new path");
168+ APPSPAWN_LOGV("SetLegacyHnpPathOrder: %{public}s", newPath);
169+ int ret = setenv("PATH", newPath, true);
170+ APPSPAWN_CHECK(ret == 0, return, "Failed to set legacy path order");
171+}
172+ 
124uint64_t DiffTime(const struct timespec *startTime, const struct timespec *endTime)173uint64_t DiffTime(const struct timespec *startTime, const struct timespec *endTime)
125{174{
126 APPSPAWN_CHECK_ONLY_EXPER(startTime != NULL, return 0);175 APPSPAWN_CHECK_ONLY_EXPER(startTime != NULL, return 0);