Commit 31385c9ce for llama.cpp
commit 31385c9cebf9e5271a38e14c26e5df6dd93718b1
Author: Adrien Gallouët <angt@huggingface.co>
Date: Tue Sep 29 13:46:53 2026 +0200
common : add fs_write_atomic() (#29642)
- Check for buffered write errors when closing downloaded files.
- Use UTF-8 paths when writing ETag files on Windows.
- Write in binary mode on Windows.
Signed-off-by: Adrien Gallouët <angt@huggingface.co>
diff --git a/common/common.cpp b/common/common.cpp
index 6d57b5421..d1e7b7e5d 100644
--- a/common/common.cpp
+++ b/common/common.cpp
@@ -912,6 +912,29 @@ std::string fs_path_to_utf8(const std::filesystem::path & path) {
return std::string(value.begin(), value.end());
}
+void fs_write_atomic(const std::filesystem::path & path, const std::string & data) {
+ std::error_code ec;
+ std::filesystem::path path_tmp = path;
+ path_tmp += ".tmp";
+
+ if (path.has_parent_path()) {
+ std::filesystem::create_directories(path.parent_path(), ec);
+ }
+
+ std::ofstream file(path_tmp, std::ios::binary);
+ file << data;
+ file.close();
+
+ if (!file.fail()) {
+ std::filesystem::rename(path_tmp, path, ec);
+ }
+
+ if (file.fail() || ec) {
+ std::filesystem::remove(path_tmp, ec);
+ throw std::runtime_error("failed to write file: " + fs_path_to_utf8(path));
+ }
+}
+
bool fs_is_directory(const std::string & path) {
std::filesystem::path dir(path);
return std::filesystem::exists(dir) && std::filesystem::is_directory(dir);
diff --git a/common/common.h b/common/common.h
index 0a09fed7f..01726247b 100644
--- a/common/common.h
+++ b/common/common.h
@@ -929,6 +929,8 @@ std::vector<common_file_info> fs_list(const std::string & path, bool include_dir
// fs open, also handle UTF8 on Windows
std::ifstream fs_open_ifstream(const std::string & fname, std::ios_base::openmode mode);
+void fs_write_atomic(const std::filesystem::path & path, const std::string & data);
+
//
// TTY utils
//
diff --git a/common/download.cpp b/common/download.cpp
index d7875078c..6b7123e47 100644
--- a/common/download.cpp
+++ b/common/download.cpp
@@ -46,39 +46,9 @@
// downloader
//
-// validate repo name format: owner/repo
-static void write_file(const std::string & fname, const std::string & content) {
- const std::string fname_tmp = fname + ".tmp";
- std::ofstream file(fname_tmp);
- if (!file) {
- throw std::runtime_error(string_format("error: failed to open file '%s'\n", fname.c_str()));
- }
-
- try {
- file << content;
- file.close();
-
- // Makes write atomic
- if (rename(fname_tmp.c_str(), fname.c_str()) != 0) {
- LOG_ERR("%s: unable to rename file: %s to %s\n", __func__, fname_tmp.c_str(), fname.c_str());
- // If rename fails, try to delete the temporary file
- if (remove(fname_tmp.c_str()) != 0) {
- LOG_ERR("%s: unable to delete temporary file: %s\n", __func__, fname_tmp.c_str());
- }
- }
- } catch (...) {
- // If anything fails, try to delete the temporary file
- if (remove(fname_tmp.c_str()) != 0) {
- LOG_ERR("%s: unable to delete temporary file: %s\n", __func__, fname_tmp.c_str());
- }
-
- throw std::runtime_error(string_format("error: failed to write file '%s'\n", fname.c_str()));
- }
-}
-
static void write_etag(const std::string & path, const std::string & etag) {
const std::string etag_path = path + ".etag";
- write_file(etag_path, etag);
+ fs_write_atomic(std::filesystem::u8path(etag_path), etag);
LOG_DBG("%s: file etag saved: %s\n", __func__, etag_path.c_str());
}
@@ -274,6 +244,12 @@ static bool common_pull_file(httplib::Client & cli,
return false;
}
+ ofs.close();
+ if (!ofs) {
+ LOG_ERR("%s: error closing file: %s\n", __func__, path_tmp.c_str());
+ return false;
+ }
+
return true;
}
diff --git a/common/hf-cache.cpp b/common/hf-cache.cpp
index 5f37084d7..4241d005a 100644
--- a/common/hf-cache.cpp
+++ b/common/hf-cache.cpp
@@ -172,29 +172,6 @@ static bool is_valid_subpath(const fs::path & path, const fs::path & subpath) {
return b_end == b.end();
}
-static void safe_write_file(const fs::path & path, const std::string & data) {
- fs::path path_tmp = path;
- path_tmp += ".tmp";
-
- if (path.has_parent_path()) {
- fs::create_directories(path.parent_path());
- }
-
- std::ofstream file(path_tmp);
- file << data;
- file.close();
-
- std::error_code ec;
-
- if (!file.fail()) {
- fs::rename(path_tmp, path, ec);
- }
- if (file.fail() || ec) {
- fs::remove(path_tmp, ec);
- throw std::runtime_error("failed to write file: " + fs_path_to_utf8(path));
- }
-}
-
static common_json api_get(const std::string & url,
const std::string & token) {
auto [cli, parts] = common_http_client(url);
@@ -282,7 +259,7 @@ static std::string get_repo_commit(const std::string & repo_id,
return {};
}
- safe_write_file(refs_path / name_path, commit);
+ fs_write_atomic(refs_path / name_path, commit);
return commit;
} catch (const common_json_error & e) {