Harden pointer head writes against ENOENT races

This commit is contained in:
Carl Niklas Rydberg 2026-02-08 08:46:06 +01:00
parent 03d970c576
commit 327812ca96

View file

@ -471,6 +471,7 @@ static amduat_asl_pointer_error_t amduat_asl_pointer_write_head(
const amduat_reference_t *prev_ref, const amduat_reference_t *prev_ref,
bool has_prev) { bool has_prev) {
char *tmp_path; char *tmp_path;
char *parent_dir = NULL;
size_t tmp_len; size_t tmp_len;
int tmp_fd; int tmp_fd;
FILE *fp; FILE *fp;
@ -511,9 +512,22 @@ static amduat_asl_pointer_error_t amduat_asl_pointer_write_head(
free((void *)prev_bytes.data); free((void *)prev_bytes.data);
return AMDUAT_ASL_POINTER_ERR_IO; return AMDUAT_ASL_POINTER_ERR_IO;
} }
parent_dir = amduat_asl_pointer_parent_dir(path);
if (parent_dir == NULL || !amduat_asl_pointer_ensure_directory(parent_dir)) {
free(parent_dir);
free(tmp_path);
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
return AMDUAT_ASL_POINTER_ERR_IO;
}
snprintf(tmp_path, tmp_len, "%s.tmp.XXXXXX", path); snprintf(tmp_path, tmp_len, "%s.tmp.XXXXXX", path);
tmp_fd = mkstemp(tmp_path); tmp_fd = mkstemp(tmp_path);
if (tmp_fd < 0 && errno == ENOENT &&
amduat_asl_pointer_ensure_directory(parent_dir)) {
tmp_fd = mkstemp(tmp_path);
}
if (tmp_fd < 0) { if (tmp_fd < 0) {
free(parent_dir);
free(tmp_path); free(tmp_path);
free((void *)ref_bytes.data); free((void *)ref_bytes.data);
free((void *)prev_bytes.data); free((void *)prev_bytes.data);
@ -575,23 +589,25 @@ static amduat_asl_pointer_error_t amduat_asl_pointer_write_head(
} }
if (err == AMDUAT_ASL_POINTER_OK && rename(tmp_path, path) != 0) { if (err == AMDUAT_ASL_POINTER_OK && rename(tmp_path, path) != 0) {
err = AMDUAT_ASL_POINTER_ERR_IO; if (errno == ENOENT && amduat_asl_pointer_ensure_directory(parent_dir) &&
rename(tmp_path, path) == 0) {
/* Recovered after recreating parent directory. */
} else {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
} }
if (err == AMDUAT_ASL_POINTER_OK) { if (err == AMDUAT_ASL_POINTER_OK) {
char *parent_dir = amduat_asl_pointer_parent_dir(path); if (!amduat_asl_pointer_fsync_directory(parent_dir)) {
if (parent_dir != NULL) { amduat_log(AMDUAT_LOG_WARN,
if (!amduat_asl_pointer_fsync_directory(parent_dir)) { "pointer fsync dir failed for %s", parent_dir);
amduat_log(AMDUAT_LOG_WARN, err = AMDUAT_ASL_POINTER_ERR_IO;
"pointer fsync dir failed for %s", parent_dir);
err = AMDUAT_ASL_POINTER_ERR_IO;
}
free(parent_dir);
} }
} }
if (err != AMDUAT_ASL_POINTER_OK) { if (err != AMDUAT_ASL_POINTER_OK) {
(void)remove(tmp_path); (void)remove(tmp_path);
} }
free(parent_dir);
free(tmp_path); free(tmp_path);
free((void *)ref_bytes.data); free((void *)ref_bytes.data);
free((void *)prev_bytes.data); free((void *)prev_bytes.data);