| 1200 | } |
| 1201 | |
| 1202 | static main_build_identity_status_t main_build_identity(cbm_daemon_build_identity_t *identity) { |
| 1203 | if (!identity) { |
| 1204 | return MAIN_BUILD_IDENTITY_INVALID_OUTPUT; |
| 1205 | } |
| 1206 | if (!cbm_index_supervisor_capture_build_fingerprint()) { |
| 1207 | return MAIN_BUILD_IDENTITY_PROCESS_FINGERPRINT; |
| 1208 | } |
| 1209 | const char *fingerprint = cbm_index_supervisor_build_fingerprint(); |
| 1210 | if (!fingerprint) { |
| 1211 | return MAIN_BUILD_IDENTITY_PROCESS_FINGERPRINT; |
| 1212 | } |
| 1213 | const char *cache = cbm_resolve_cache_dir(); |
| 1214 | char canonical_cache[MAIN_PATH_CAP]; |
| 1215 | static char cache_fingerprint[CBM_SHA256_HEX_LEN + 1]; |
| 1216 | if (!cache || !cache[0]) { |
| 1217 | return MAIN_BUILD_IDENTITY_CACHE_RESOLVE; |
| 1218 | } |
| 1219 | /* Preserve one intentional alias spelling at the process boundary: an |
| 1220 | * existing directory (including a symlink supplied by the user) is |
| 1221 | * resolved first. Only a genuinely absent root goes through mkdir_p's |
| 1222 | * component-by-component no-follow creation path. The process then uses |
| 1223 | * only the resulting canonical path, so retargeting the original alias |
| 1224 | * cannot move storage after cohort admission. */ |
| 1225 | bool cache_ready = cbm_canonical_path(cache, canonical_cache, sizeof(canonical_cache)); |
| 1226 | if (!cache_ready && cbm_mkdir_p(cache, 0700)) { |
| 1227 | cache_ready = cbm_canonical_path(cache, canonical_cache, sizeof(canonical_cache)); |
| 1228 | } |
| 1229 | if (!cache_ready || !cbm_is_dir(canonical_cache)) { |
| 1230 | return MAIN_BUILD_IDENTITY_CACHE_CANONICALIZE; |
| 1231 | } |
| 1232 | cbm_normalize_path_sep(canonical_cache); |
| 1233 | /* Admission is account-scoped, so its storage authority must be too. |
| 1234 | * Harden the canonical object before hashing it. Replacement of this |
| 1235 | * owner-only path by the same already-compromised OS account is outside |
| 1236 | * the v1 threat boundary; cross-account and unsafe filesystem states fail |
| 1237 | * here before any daemon/cohort state is opened. */ |
| 1238 | if (!cbm_daemon_ipc_private_directory_secure(canonical_cache)) { |
| 1239 | return MAIN_BUILD_IDENTITY_CACHE_PRIVATE; |
| 1240 | } |
| 1241 | /* Every cache consumer in this process must use the exact path whose |
| 1242 | * fingerprint joins the account-wide cohort. Keeping an original symlink |
| 1243 | * spelling in the environment would let a later retarget move storage |
| 1244 | * while the process still advertises the old canonical root. */ |
| 1245 | if (cbm_setenv("CBM_CACHE_DIR", canonical_cache, 1) != 0) { |
| 1246 | return MAIN_BUILD_IDENTITY_CACHE_ENVIRONMENT; |
| 1247 | } |
| 1248 | cbm_sha256_hex(canonical_cache, strlen(canonical_cache), cache_fingerprint); |
| 1249 | *identity = (cbm_daemon_build_identity_t){ |
| 1250 | .semantic_version = CBM_VERSION, |
| 1251 | .build_fingerprint = fingerprint, |
| 1252 | .cache_fingerprint = cache_fingerprint, |
| 1253 | .protocol_abi = CBM_DAEMON_RUNTIME_WIRE_ABI, |
| 1254 | .store_abi = 1, |
| 1255 | .feature_abi = 1, |
| 1256 | }; |
| 1257 | return MAIN_BUILD_IDENTITY_OK; |
| 1258 | } |
| 1259 |
no test coverage detected