Skip to content

Commit f36ab45

Browse files
committed
fix(store): normalize the Windows file-API path on every SQLite open
Root cause of the Cyrillic guard failure, found via the finalize diagnostics: seal_existing rc=-1. The primary store open normalizes the path through cbm_path_for_file_api (UTF-8 -> wide -> UTF-8, the form SQLite's Windows VFS needs), but four maintenance opens passed the raw UTF-8 path: cbm_store_seal_existing_path_for_replace (the one this path hits) cbm_store_prepare_path_for_replace cbm_store_backup_path (both source and staging) cbm_store_dump_to_file So a generation was WRITTEN through the normalized path and then could not be RE-OPENED through the raw one. On the second index into a cache the seal step failed, finalize returned PERSIST_FAILED, and the user was told "Check repo_path exists and contains source files" about a repo that had just indexed cleanly (nodes=12 edges=21 in the same worker log). On POSIX cbm_path_for_file_api is a no-op memcpy, which is why the full 3-OS ladder and every non-Windows test passed -- the defect is Windows-only and only manifests on a path the raw UTF-8 -> SQLite open mishandles. It surfaced on the Cyrillic case first because the guard indexes the same repo TWICE (once for the count, once on retry), making it the first case to re-open an existing generation; the earlier ASCII/latin1 cases index into fresh caches. All four now use cbm_path_for_file_api exactly as the primary open does. Also upgrades dump_to_file from sqlite3_open to sqlite3_open_v2 with explicit flags. The finalize.prepare_failed / rename_failed diagnostics from the previous commit stay: a silent PERSIST_FAILED that blamed the repo cost a day of remote bisection and must never be silent again. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
1 parent 601dcbf commit f36ab45

1 file changed

Lines changed: 29 additions & 5 deletions

File tree

‎src/store/store.c‎

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,8 +1215,12 @@ int cbm_store_prepare_path_for_replace(const char *path) {
12151215
if (!path) {
12161216
return CBM_STORE_ERR;
12171217
}
1218+
char prepare_path[4096];
1219+
if (!cbm_path_for_file_api(path, prepare_path, sizeof(prepare_path))) {
1220+
return CBM_STORE_ERR;
1221+
}
12181222
sqlite3 *db = NULL;
1219-
int rc = sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE, NULL);
1223+
int rc = sqlite3_open_v2(prepare_path, &db, SQLITE_OPEN_READWRITE, NULL);
12201224
if (rc != SQLITE_OK) {
12211225
sqlite3_close(db);
12221226
return CBM_STORE_ERR;
@@ -1237,15 +1241,21 @@ int cbm_store_backup_path(const char *source_path, const char *staging_path) {
12371241
return CBM_STORE_ERR;
12381242
}
12391243

1244+
char source_api[4096];
1245+
char staging_api[4096];
1246+
if (!cbm_path_for_file_api(source_path, source_api, sizeof(source_api)) ||
1247+
!cbm_path_for_file_api(staging_path, staging_api, sizeof(staging_api))) {
1248+
return CBM_STORE_ERR;
1249+
}
12401250
sqlite3 *source = NULL;
12411251
sqlite3 *dest = NULL;
1242-
int rc = sqlite3_open_v2(source_path, &source, SQLITE_OPEN_READONLY, NULL);
1252+
int rc = sqlite3_open_v2(source_api, &source, SQLITE_OPEN_READONLY, NULL);
12431253
if (rc != SQLITE_OK) {
12441254
sqlite3_close(source);
12451255
return CBM_STORE_ERR;
12461256
}
12471257
sqlite3_busy_timeout(source, 10000);
1248-
rc = sqlite3_open_v2(staging_path, &dest, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
1258+
rc = sqlite3_open_v2(staging_api, &dest, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
12491259
if (rc != SQLITE_OK) {
12501260
sqlite3_close(dest);
12511261
sqlite3_close(source);
@@ -1354,8 +1364,17 @@ int cbm_store_seal_existing_path_for_replace(const char *db_path) {
13541364
return CBM_STORE_ERR;
13551365
}
13561366

1367+
/* Normalize to the Windows file-API form exactly as the primary open does
1368+
* (cbm_store_open_internal). SQLite's win VFS needs the wide-round-tripped
1369+
* path; the raw UTF-8 string fails to open a file that was WRITTEN through
1370+
* the normalized form, so re-sealing a just-indexed generation returned -1
1371+
* and finalize misreported it as "repo has no source files". */
1372+
char seal_path[4096];
1373+
if (!cbm_path_for_file_api(db_path, seal_path, sizeof(seal_path))) {
1374+
return CBM_STORE_ERR;
1375+
}
13571376
cbm_store_t maintenance = {0};
1358-
int rc = sqlite3_open_v2(db_path, &maintenance.db, SQLITE_OPEN_READWRITE, NULL);
1377+
int rc = sqlite3_open_v2(seal_path, &maintenance.db, SQLITE_OPEN_READWRITE, NULL);
13591378
if (rc != SQLITE_OK) {
13601379
int primary = rc & 0xff;
13611380
sqlite3_close(maintenance.db);
@@ -1427,8 +1446,13 @@ int cbm_store_dump_to_file(cbm_store_t *s, const char *dest_path) {
14271446
snprintf(tmp_path, sizeof(tmp_path), "%s.tmp", dest_path);
14281447
(void)unlink(tmp_path);
14291448

1449+
char tmp_api[4096];
1450+
if (!cbm_path_for_file_api(tmp_path, tmp_api, sizeof(tmp_api))) {
1451+
store_set_error(s, "dump: cannot normalize temp file path");
1452+
return CBM_STORE_ERR;
1453+
}
14301454
sqlite3 *dest_db = NULL;
1431-
int rc = sqlite3_open(tmp_path, &dest_db);
1455+
int rc = sqlite3_open_v2(tmp_api, &dest_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
14321456
if (rc != SQLITE_OK) {
14331457
store_set_error(s, "dump: cannot open temp file");
14341458
return CBM_STORE_ERR;

0 commit comments

Comments
 (0)