117 const std::filesystem::path& requested_path) {
118 std::filesystem::path candidate = requested_path;
119 for (
int suffix = 1; suffix <= 1000; ++suffix) {
120 std::error_code exists_ec;
121 const bool exists = std::filesystem::exists(candidate, exists_ec);
123 return absl::InternalError(absl::StrCat(
124 "Could not inspect required ROM backup path: ", candidate.string(),
125 ": ", exists_ec.message()));
130 candidate = requested_path.string() +
"_" + std::to_string(suffix);
133 return absl::ResourceExhaustedError(
134 "Could not allocate a unique required ROM backup path");
143 const std::filesystem::path& source_path,
144 const std::filesystem::path& requested_backup_path) {
146 if (!backup_path_or.ok()) {
147 return backup_path_or.status();
150 const std::filesystem::path backup_path = *backup_path_or;
151 std::filesystem::path temp_path = backup_path;
154 std::error_code copy_ec;
155 const bool copied = std::filesystem::copy_file(
156 source_path, temp_path, std::filesystem::copy_options::none, copy_ec);
157 if (!copied || copy_ec) {
158 std::error_code cleanup_ec;
159 std::filesystem::remove(temp_path, cleanup_ec);
160 return absl::FailedPreconditionError(absl::StrCat(
161 "Could not create required ROM backup: ", source_path.string(),
" -> ",
162 backup_path.string(),
": ",
163 copy_ec ? copy_ec.message() :
"copy did not complete"));
166#if !defined(__EMSCRIPTEN__)
170 std::error_code rename_ec;
171 std::filesystem::rename(temp_path, backup_path, rename_ec);
173 std::error_code cleanup_ec;
174 std::filesystem::remove(temp_path, cleanup_ec);
175 return absl::FailedPreconditionError(absl::StrCat(
176 "Could not finalize required ROM backup: ", backup_path.string(),
": ",
177 rename_ec.message()));
180#if !defined(__EMSCRIPTEN__)
184 return absl::OkStatus();
194 const std::filesystem::path& target_path,
195 const std::vector<uint8_t>& bytes) {
197 return absl::InternalError(
198 "Could not create temp ROM file: staging failure injected for testing");
200 static std::atomic<uint64_t> sequence{0};
201 for (
int attempt = 0; attempt < 100; ++attempt) {
202 const auto tick =
static_cast<uint64_t
>(
203 std::chrono::steady_clock::now().time_since_epoch().count());
204 const auto id = sequence.fetch_add(1, std::memory_order_relaxed);
205 const auto temp_path =
206 target_path.parent_path() /
207 absl::StrFormat(
".yaze-rom-%016x-%016x.tmp", tick,
id);
210 CreateFileW(temp_path.wstring().c_str(), GENERIC_WRITE, 0,
nullptr,
211 CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
nullptr);
212 if (file == INVALID_HANDLE_VALUE) {
213 const DWORD error = GetLastError();
214 if (error == ERROR_FILE_EXISTS || error == ERROR_ALREADY_EXISTS) {
217 return absl::InternalError(absl::StrCat(
218 "Could not create temp ROM file: ", temp_path.string(),
": ",
219 std::error_code(
static_cast<int>(error), std::system_category())
223 int file = open(temp_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0666);
225 const int error = errno;
226 if (error == EEXIST) {
229 return absl::InternalError(absl::StrCat(
230 "Could not create temp ROM file: ", temp_path.string(),
": ",
231 std::error_code(error, std::generic_category()).message()));
236 absl::Cleanup cleanup = [&] {
238 if (file != INVALID_HANDLE_VALUE) {
239 (void)CloseHandle(file);
247 std::filesystem::remove(temp_path, ec);
251 while (offset < bytes.size()) {
252 const size_t chunk = std::min<size_t>(bytes.size() - offset, 1024 * 1024);
255 const bool ok = WriteFile(file, bytes.data() + offset,
256 static_cast<DWORD
>(chunk), &written,
nullptr);
258 ok ? ERROR_WRITE_FAULT :
static_cast<int>(GetLastError());
259 if (!ok || written == 0) {
260 return absl::InternalError(absl::StrCat(
261 "Error while writing ROM file: ", temp_path.string(),
": ",
262 std::error_code(error, std::system_category()).message()));
265 const ssize_t written = write(file, bytes.data() + offset, chunk);
266 if (written < 0 && errno == EINTR) {
270 const int error = written < 0 ? errno : EIO;
271 return absl::InternalError(absl::StrCat(
272 "Error while writing ROM file: ", temp_path.string(),
": ",
273 std::error_code(error, std::generic_category()).message()));
276 offset +=
static_cast<size_t>(written);
280 if (!FlushFileBuffers(file)) {
281 const int error =
static_cast<int>(GetLastError());
282 return absl::InternalError(absl::StrCat(
283 "Could not flush temp ROM file: ", temp_path.string(),
": ",
284 std::error_code(error, std::system_category()).message()));
286 const bool closed = CloseHandle(file);
287 const int close_error = closed ? 0 :
static_cast<int>(GetLastError());
288 file = INVALID_HANDLE_VALUE;
289 const auto close_category = &std::system_category();
291#if !defined(__EMSCRIPTEN__)
294 flush_result = fsync(file);
295 }
while (flush_result != 0 && errno == EINTR);
296 if (flush_result != 0) {
297 const int error = errno;
298 return absl::InternalError(absl::StrCat(
299 "Could not flush temp ROM file: ", temp_path.string(),
": ",
300 std::error_code(error, std::generic_category()).message()));
303 const int close_result = close(file);
304 const int close_error = close_result == 0 ? 0 : errno;
306 const auto close_category = &std::generic_category();
308 if (close_error != 0) {
309 return absl::InternalError(absl::StrCat(
310 "Could not close temp ROM file: ", temp_path.string(),
": ",
311 std::error_code(close_error, *close_category).message()));
313 std::move(cleanup).Cancel();
316 return absl::ResourceExhaustedError(
317 "Could not allocate a unique temp ROM file");
339 CreateFileW(path.wstring().c_str(), GENERIC_WRITE,
340 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
341 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr);
342 if (handle == INVALID_HANDLE_VALUE) {
345 (void)FlushFileBuffers(handle);
346 (void)CloseHandle(handle);
348 int fd = open(path.c_str(), O_RDONLY);