Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions src/foundation/compat_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1039,9 +1039,54 @@ int cbm_rename_replace(const char *src, const char *dst) {
wchar_t *wdst = cbm_path_to_wide(dst);
int ret = CBM_NOT_FOUND;
if (wsrc && wdst) {
ret = MoveFileExW(wsrc, wdst, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)
? 0
: CBM_NOT_FOUND;
if (MoveFileExW(wsrc, wdst, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
ret = 0;
} else {
/* Translate the Win32 error into errno so callers can report WHY.
*
* Callers log `errno` after a failed rename (see
* finalize.rename_failed in the pipeline). Without this the value
* is whatever happened to be left there by an unrelated CRT call,
* so on Windows the one field that should explain an atomic-publish
* failure was noise. #1620 is exactly that: an ACL problem surfaced
* to the user as "Pipeline failed. Check repo_path exists and
* contains source files" — blaming their repository — because
* ERROR_ACCESS_DENIED never reached the log.
*
* ERROR_ACCESS_DENIED is the interesting one here: MoveFileEx needs
* DELETE on the destination, which a cache file created under an
* empty or foreign DACL does not grant. */
DWORD error = GetLastError();
switch (error) {
case ERROR_ACCESS_DENIED:
case ERROR_WRITE_PROTECT:
errno = EACCES;
break;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
errno = ENOENT;
break;
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
case ERROR_USER_MAPPED_FILE:
errno = EBUSY;
break;
case ERROR_NOT_SAME_DEVICE:
errno = EXDEV;
break;
case ERROR_DISK_FULL:
errno = ENOSPC;
break;
case ERROR_INVALID_NAME:
case ERROR_FILENAME_EXCED_RANGE:
errno = ENAMETOOLONG;
break;
default:
errno = EIO;
break;
}
ret = CBM_NOT_FOUND;
}
}
free(wsrc);
free(wdst);
Expand Down
9 changes: 9 additions & 0 deletions src/pipeline/pipeline.c
Original file line number Diff line number Diff line change
Expand Up @@ -1806,6 +1806,15 @@ int cbm_pipeline_finalize_staged_generation(char *stage_path, const char *final_
struct timespec t_fin;
cbm_clock_gettime(CLOCK_MONOTONIC, &t_fin);
if (cbm_remove_db_sidecars(stage_path) != 0) {
/* This returned PERSIST_FAILED with no log at all, which is how #1620
* presented: every pass succeeded, the worker exited 0, no error-level
* line was emitted anywhere, and the user was told "Pipeline failed.
* Check repo_path exists and contains source files" — pointed at their
* repository for a filesystem permission problem. A publish that fails
* must say so. */
char errno_text[16];
(void)snprintf(errno_text, sizeof(errno_text), "%d", errno);
cbm_log_error("finalize.sidecar_removal_failed", "errno", errno_text, "stage", stage_path);
discard_generation_stage(stage_path);
return CBM_PIPELINE_PERSIST_FAILED;
}
Expand Down
Loading