mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 18:31:11 +00:00
Retry a Failed Lock File, Keep Special Targets in Place and Sweep Stale Temporaries
A lock file that failed to open once stayed unopened for the rest of the process, so a scanner holding the fresh file for a moment on Windows silently disabled the lock for the session, and a lock call that throws, as it does on a share without a lock service, was re-attempted and logged by every guard. Both are now left alone for the cool-down and then retried. Renaming a fresh file over the target turned a symlinked preset into a plain file, reset its permissions to the umask default, and could not work at all for a settings export to a device or pipe, since a temporary cannot be created beside /dev/stdout. A target that is not a regular file is now written in place, an existing target keeps its permissions, and a directory that refuses the temporary but not the file falls back too. The inline PhysicalPrinter::save() overload was the one physical printer writer without the guard. load_info() took the lock redundantly under the scan guard and, in read-only mode, created user.lock per file; the bundle metadata reader now honours read_only too. The plugin config, the setup guide's profile cache and the vendor preset cache wrote through a temporary by hand; they use the helper, with a binary mode for the cache. A crash between temporary and rename left a <name>.<pid>.tmp behind for good; AppConfig::load() and the preset scan remove stale ones while they hold the lock, and AppConfig::save() names its temporary the same way.
This commit is contained in:
+45
-5
@@ -711,10 +711,10 @@ std::error_code rename_file(const std::string &from, const std::string &to)
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::error_code write_whole_file(const std::string &path, const std::string &content)
|
||||
static std::error_code write_whole_file(const std::string &path, const std::string &content, bool binary)
|
||||
{
|
||||
errno = 0;
|
||||
boost::nowide::ofstream out(path, std::ios::out | std::ios::trunc);
|
||||
boost::nowide::ofstream out(path, std::ios::out | std::ios::trunc | (binary ? std::ios::binary : std::ios::openmode{}));
|
||||
out << content;
|
||||
out.close();
|
||||
if (! out.fail())
|
||||
@@ -722,13 +722,24 @@ static std::error_code write_whole_file(const std::string &path, const std::stri
|
||||
return std::make_error_code(errno != 0 ? static_cast<std::errc>(errno) : std::errc::io_error);
|
||||
}
|
||||
|
||||
std::error_code write_file_atomically(const std::string &path, const std::string &content)
|
||||
std::error_code write_file_atomically(const std::string &path, const std::string &content, bool binary)
|
||||
{
|
||||
boost::system::error_code bec;
|
||||
const boost::filesystem::file_status target = boost::filesystem::symlink_status(path, bec);
|
||||
const bool target_exists = ! bec && boost::filesystem::exists(target);
|
||||
if (target_exists && ! boost::filesystem::is_regular_file(target))
|
||||
return write_whole_file(path, content, binary);
|
||||
|
||||
const std::string tmp_path = path + "." + std::to_string(get_current_pid()) + ".tmp";
|
||||
if (std::error_code ec = write_whole_file(tmp_path, content)) {
|
||||
if (std::error_code ec = write_whole_file(tmp_path, content, binary)) {
|
||||
boost::nowide::remove(tmp_path.c_str());
|
||||
// A directory that allows writing the file but not creating one beside it.
|
||||
if (target_exists && ec == std::errc::permission_denied)
|
||||
return write_whole_file(path, content, binary);
|
||||
return ec;
|
||||
}
|
||||
if (target_exists)
|
||||
boost::filesystem::permissions(tmp_path, target.permissions(), bec);
|
||||
std::error_code ec = rename_file(tmp_path, path);
|
||||
if (ec)
|
||||
boost::nowide::remove(tmp_path.c_str());
|
||||
@@ -739,12 +750,41 @@ std::error_code write_file_atomically(const std::string &path, const std::string
|
||||
// in place the way this used to work before the atomic path existed.
|
||||
if (ec == std::errc::permission_denied) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Cannot replace " << path << " while another process holds it open; writing in place";
|
||||
ec = write_whole_file(path, content);
|
||||
ec = write_whole_file(path, content, binary);
|
||||
}
|
||||
#endif
|
||||
return ec;
|
||||
}
|
||||
|
||||
size_t remove_stale_temp_files(const boost::filesystem::path &dir, const std::string &name_prefix)
|
||||
{
|
||||
// <anything>.<digits>.tmp
|
||||
auto is_temp_name = [&name_prefix](const std::string &name) {
|
||||
if (name.compare(0, name_prefix.size(), name_prefix) != 0)
|
||||
return false;
|
||||
static const std::string suffix = ".tmp";
|
||||
if (name.size() <= suffix.size() || name.compare(name.size() - suffix.size(), suffix.size(), suffix) != 0)
|
||||
return false;
|
||||
const size_t digits_end = name.size() - suffix.size();
|
||||
const size_t dot = name.rfind('.', digits_end - 1);
|
||||
if (dot == std::string::npos || dot == 0 || dot + 1 == digits_end)
|
||||
return false;
|
||||
return std::all_of(name.begin() + dot + 1, name.begin() + digits_end, [](char c) { return c >= '0' && c <= '9'; });
|
||||
};
|
||||
size_t removed = 0;
|
||||
boost::system::error_code ec;
|
||||
for (boost::filesystem::directory_iterator it(dir, ec), end; ! ec && it != end; it.increment(ec)) {
|
||||
if (! boost::filesystem::is_regular_file(it->symlink_status()) || ! is_temp_name(it->path().filename().string()))
|
||||
continue;
|
||||
if (boost::filesystem::remove(it->path(), ec)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Removed stale temporary file " << it->path();
|
||||
++ removed;
|
||||
}
|
||||
ec.clear();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
// Copied from boost::filesystem.
|
||||
// Called by copy_file_linux() in case linux sendfile() API is not supported.
|
||||
|
||||
Reference in New Issue
Block a user