| 524 | } |
| 525 | |
| 526 | int fs_remove(const char *filename) |
| 527 | { |
| 528 | #if defined(CONF_FAMILY_WINDOWS) |
| 529 | if(fs_is_dir(filename)) |
| 530 | { |
| 531 | // Not great, but otherwise using this function on a folder would only rename the folder but fail to delete it. |
| 532 | return 1; |
| 533 | } |
| 534 | const std::wstring wide_filename = windows_utf8_to_wide(filename); |
| 535 | |
| 536 | unsigned random_num; |
| 537 | secure_random_fill(&random_num, sizeof(random_num)); |
| 538 | std::wstring wide_filename_temp; |
| 539 | do |
| 540 | { |
| 541 | char suffix[64]; |
| 542 | str_format(suffix, sizeof(suffix), ".%08X.toberemoved", random_num); |
| 543 | wide_filename_temp = wide_filename + windows_utf8_to_wide(suffix); |
| 544 | ++random_num; |
| 545 | } while(GetFileAttributesW(wide_filename_temp.c_str()) != INVALID_FILE_ATTRIBUTES); |
| 546 | |
| 547 | // The DeleteFileW function only marks the file for deletion but the deletion may not take effect immediately, which can |
| 548 | // cause subsequent operations using this filename to fail until all handles are closed. The MoveFileExW function with the |
| 549 | // MOVEFILE_WRITE_THROUGH flag is guaranteed to wait for the file to be moved on disk, so we first rename the file to be |
| 550 | // deleted to a random temporary name and then mark that for deletion, to ensure that the filename is usable immediately. |
| 551 | if(MoveFileExW(wide_filename.c_str(), wide_filename_temp.c_str(), MOVEFILE_WRITE_THROUGH) == 0) |
| 552 | { |
| 553 | const DWORD error = GetLastError(); |
| 554 | if(error == ERROR_FILE_NOT_FOUND) |
| 555 | { |
| 556 | return 0; // Success: Renaming failed because the original file did not exist. |
| 557 | } |
| 558 | const std::string filename_temp = windows_wide_to_utf8(wide_filename_temp.c_str()).value_or("(invalid filename)"); |
| 559 | log_error("filesystem", "Failed to rename file '%s' to '%s' for removal (%ld '%s')", filename, filename_temp.c_str(), error, windows_format_system_message(error).c_str()); |
| 560 | return 1; |
| 561 | } |
| 562 | if(DeleteFileW(wide_filename_temp.c_str()) != 0) |
| 563 | { |
| 564 | return 0; // Success: Marked the renamed file for deletion successfully. |
| 565 | } |
| 566 | const DWORD error = GetLastError(); |
| 567 | if(error == ERROR_FILE_NOT_FOUND) |
| 568 | { |
| 569 | return 0; // Success: Another process deleted the renamed file we were about to delete?! |
| 570 | } |
| 571 | const std::string filename_temp = windows_wide_to_utf8(wide_filename_temp.c_str()).value_or("(invalid filename)"); |
| 572 | log_error("filesystem", "Failed to remove file '%s' (%ld '%s')", filename_temp.c_str(), error, windows_format_system_message(error).c_str()); |
| 573 | // Success: While the temporary could not be deleted, this is also considered success because the original file does not exist anymore. |
| 574 | // Callers of this function expect that the original file does not exist anymore if and only if the function succeeded. |
| 575 | return 0; |
| 576 | #else |
| 577 | if(unlink(filename) == 0 || errno == ENOENT) |
| 578 | { |
| 579 | return 0; |
| 580 | } |
| 581 | log_error("filesystem", "Failed to remove file '%s' (%d '%s')", filename, errno, strerror(errno)); |
| 582 | return 1; |
| 583 | #endif |