if (m_stream) {
// commit() was not called so remove the lingering temporary file.
fclose(m_stream);
- tmp_unlink(m_tmp_path.c_str());
+ Util::unlink_tmp(m_tmp_path);
}
}
int result = fclose(m_stream);
m_stream = nullptr;
if (result == EOF) {
- tmp_unlink(m_tmp_path.c_str());
+ Util::unlink_tmp(m_tmp_path);
throw Error(
fmt::format("failed to write data to {}: {}", m_path, strerror(errno)));
}
} else {
// The lock seems to be stale -- break it and try again.
cc_log("lockfile_acquire: breaking %s", lockfile.c_str());
- if (tmp_unlink(lockfile.c_str()) != 0) {
+ if (!Util::unlink_tmp(lockfile)) {
cc_log("Failed to unlink %s: %s", lockfile.c_str(), strerror(errno));
return false;
}
if (acquired()) {
cc_log("Releasing lock %s", m_lockfile.c_str());
#ifndef _WIN32
- if (tmp_unlink(m_lockfile.c_str()) != 0) {
+ if (!Util::unlink_tmp(m_lockfile)) {
cc_log("Failed to unlink %s: %s", m_lockfile.c_str(), strerror(errno));
}
#else
#include "Config.hpp"
#include "Context.hpp"
#include "FormatNonstdStringView.hpp"
+#include "logging.hpp"
#ifdef _WIN32
# include "win32compat.hpp"
}
}
+bool
+unlink_safe(const std::string& path, UnlinkLog unlink_log)
+{
+ int saved_errno = 0;
+
+ // If path is on an NFS share, unlink isn't atomic, so we rename to a temp
+ // file. We don't care if the temp file is trashed, so it's always safe to
+ // unlink it first.
+ std::string tmp_name = path + ".ccache.rm.tmp";
+
+ bool success = true;
+ if (x_rename(path.c_str(), tmp_name.c_str()) != 0) {
+ success = false;
+ saved_errno = errno;
+ } else if (unlink(tmp_name.c_str()) != 0) {
+ // It's OK if it was unlinked in a race.
+ if (errno != ENOENT && errno != ESTALE) {
+ success = false;
+ saved_errno = errno;
+ }
+ }
+
+ if (success || unlink_log == UnlinkLog::log_failure) {
+ cc_log("Unlink %s via %s", path.c_str(), tmp_name.c_str());
+ if (!success) {
+ cc_log("Unlink failed: %s", strerror(saved_errno));
+ }
+ }
+
+ errno = saved_errno;
+ return success;
+}
+
+bool
+unlink_tmp(const std::string& path, UnlinkLog unlink_log)
+{
+ int saved_errno = 0;
+
+ bool success =
+ unlink(path.c_str()) == 0 || (errno == ENOENT || errno == ESTALE);
+ if (success || unlink_log == UnlinkLog::log_failure) {
+ cc_log("Unlink %s", path.c_str());
+ if (!success) {
+ cc_log("Unlink failed: %s", strerror(saved_errno));
+ }
+ }
+
+ errno = saved_errno;
+ return success;
+}
+
void
wipe_path(const std::string& path)
{
using TraverseVisitor =
std::function<void(const std::string& path, bool is_dir)>;
+enum class UnlinkLog { log_failure, ignore_failure };
+
// Get base name of path.
nonstd::string_view base_name(nonstd::string_view path);
// Throws Error on error.
void traverse(const std::string& path, const TraverseVisitor& visitor);
+// Remove `path` (non-directory), NFS safe. Logs according to `unlink_log`.
+//
+// Returns whether removal was successful. A non-existing `path` is considered
+// successful.
+bool unlink_safe(const std::string& path,
+ UnlinkLog unlink_log = UnlinkLog::log_failure);
+
+// Remove `path` (non-directory), NFS hazardous. Use only for files that will
+// not exist on other systems. Logs according to `unlink_log`.
+//
+// Returns whether removal was successful. A non-existing `path` is considered
+// successful.
+bool unlink_tmp(const std::string& path,
+ UnlinkLog unlink_log = UnlinkLog::log_failure);
+
// Remove `path` (and its contents if it's a directory). A non-existing path is
// not considered an error.
//
}
auto st = Stat::lstat(path, Stat::OnError::log);
if (st && st.mtime() + k_tempdir_cleanup_interval < now) {
- tmp_unlink(path.c_str());
+ Util::unlink_tmp(path);
}
});
}
" usage",
tmp_file.c_str(),
e.what());
- x_unlink(tmp_file.c_str());
+ Util::unlink_safe(tmp_file);
return;
}
tmp_file.c_str(),
output_dep.c_str(),
strerror(errno));
- x_unlink(tmp_file.c_str());
+ Util::unlink_safe(tmp_file);
} else {
cc_log("Renamed dependency file: %s -> %s",
tmp_file.c_str(),
// Workaround for Clang bug where it overwrites an existing object file
// when it's compiling an assembler file, see
// <https://bugs.llvm.org/show_bug.cgi?id=39782>.
- x_unlink(ctx.args_info.output_obj.c_str());
+ Util::unlink_safe(ctx.args_info.output_obj);
}
if (ctx.args_info.generating_diagnostics) {
// produced one, intentionally not using x_unlink or tmp_unlink since we're
// not interested in logging successful deletions or failures due to
// non-existent .dwo files.
- if (unlink(ctx.args_info.output_dwo.c_str()) == -1 && errno != ENOENT) {
+ if (unlink(ctx.args_info.output_dwo.c_str()) != 0 && errno != ENOENT
+ && errno != ESTALE) {
cc_log("Failed to unlink %s: %s",
ctx.args_info.output_dwo.c_str(),
strerror(errno));
auto st = Stat::stat(tmp_stdout, Stat::OnError::log);
if (!st) {
// The stdout file was removed - cleanup in progress? Better bail out.
- tmp_unlink(tmp_stdout);
- tmp_unlink(tmp_stderr);
+ Util::unlink_tmp(tmp_stdout);
+ Util::unlink_tmp(tmp_stderr);
failed(STATS_MISSING);
}
// __________Using # distcc servers in pump mode
if (st.size() != 0 && ctx.guessed_compiler != GuessedCompiler::pump) {
cc_log("Compiler produced stdout");
- tmp_unlink(tmp_stdout);
- tmp_unlink(tmp_stderr);
+ Util::unlink_tmp(tmp_stdout);
+ Util::unlink_tmp(tmp_stderr);
failed(STATS_STDOUT);
}
- tmp_unlink(tmp_stdout);
+ Util::unlink_tmp(tmp_stdout);
// Merge stderr from the preprocessor (if any) and stderr from the real
// compiler into tmp_stderr.
close(fd_cpp_stderr);
close(fd_real_stderr);
close(fd_result);
- tmp_unlink(tmp_stderr2);
+ Util::unlink_tmp(tmp_stderr2);
free(tmp_stderr2);
}
// We can output stderr immediately instead of rerunning the compiler.
copy_fd(fd, 2);
close(fd);
- tmp_unlink(tmp_stderr);
+ Util::unlink_tmp(tmp_stderr);
failed(STATS_STATUS, status);
}
- tmp_unlink(tmp_stderr);
+ Util::unlink_tmp(tmp_stderr);
failed(STATS_STATUS);
}
// previous ccache versions.
if (getpid() % 1000 == 0) {
char* path = format("%s/CACHEDIR.TAG", ctx.config.cache_dir().c_str());
- x_unlink(path);
+ Util::unlink_safe(path);
free(path);
}
}
// Everything OK.
send_cached_stderr(tmp_stderr);
- tmp_unlink(tmp_stderr);
+ Util::unlink_tmp(tmp_stderr);
free(tmp_stderr);
free(tmp_stdout);
bool ok = result_get(ctx, ctx.result_path(), result_file_map);
if (!ok) {
cc_log("Failed to get result from cache");
- tmp_unlink(tmp_stderr);
+ Util::unlink_tmp(tmp_stderr);
free(tmp_stderr);
return nullopt;
}
send_cached_stderr(tmp_stderr);
- tmp_unlink(tmp_stderr);
+ Util::unlink_tmp(tmp_stderr);
free(tmp_stderr);
cc_log("Succeeded getting cached result");
cc_log("Hash from manifest doesn't match preprocessor output");
cc_log("Likely reason: different CCACHE_BASEDIRs used");
cc_log("Removing manifest as a safety measure");
- x_unlink(ctx.manifest_path().c_str());
+ Util::unlink_safe(ctx.manifest_path());
put_result_in_manifest = true;
}
#include "CacheFile.hpp"
#include "Config.hpp"
+#include "Util.hpp"
#include "logging.hpp"
#include "stats.hpp"
uint64_t* cache_size,
uint32_t* files_in_cache)
{
- bool deleted = x_try_unlink(path.c_str()) == 0;
+ bool deleted = Util::unlink_safe(path, Util::UnlinkLog::ignore_failure);
if (!deleted && errno != ENOENT && errno != ESTALE) {
cc_log("Failed to unlink %s (%s)", path.c_str(), strerror(errno));
} else if (cache_size && files_in_cache) {
// Delete any tmp files older than 1 hour right away.
if (file->lstat().mtime() + 3600 < current_time
&& Util::base_name(file->path()).find(".tmp.") != std::string::npos) {
- x_unlink(file->path().c_str());
+ Util::unlink_tmp(file->path());
continue;
}
subdir, [&](double progress) { progress_receiver(progress / 2); }, files);
for (size_t i = 0; i < files.size(); ++i) {
- x_unlink(files[i]->path().c_str());
+ Util::unlink_safe(files[i]->path());
progress_receiver(0.5 + 0.5 * i / files.size());
}
fclose(fp);
snprintf(atfile, sizeof(atfile), "\"@%s\"", tmp_file);
ret = CreateProcess(NULL, atfile, NULL, NULL, 1, 0, NULL, NULL, &si, &pi);
- tmp_unlink(tmp_file);
+ Util::unlink_tmp(tmp_file);
free(tmp_file);
}
if (!ret) {
{
bool ok = copy_file(src, dest, false);
if (ok) {
- x_unlink(src);
+ Util::unlink_safe(src);
}
return ok;
}
#endif
}
-// Remove path, NFS hazardous. Use only for temporary files that will not exist
-// on other systems. That is, the path should include tmp_string().
-int
-tmp_unlink(const char* path)
-{
- cc_log("Unlink %s", path);
- int rc = unlink(path);
- if (rc) {
- cc_log("Unlink failed: %s", strerror(errno));
- }
- return rc;
-}
-
-static int
-do_x_unlink(const char* path, bool log_failure)
-{
- int saved_errno = 0;
-
- // If path is on an NFS share, unlink isn't atomic, so we rename to a temp
- // file. We don't care if the temp file is trashed, so it's always safe to
- // unlink it first.
- char* tmp_name = format("%s.ccache.rm.tmp", path);
-
- int result = 0;
- if (x_rename(path, tmp_name) == -1) {
- result = -1;
- saved_errno = errno;
- goto out;
- }
- if (unlink(tmp_name) == -1) {
- // If it was released in a race, that's OK.
- if (errno != ENOENT && errno != ESTALE) {
- result = -1;
- saved_errno = errno;
- }
- }
-
-out:
- if (result == 0 || log_failure) {
- cc_log("Unlink %s via %s", path, tmp_name);
- if (result != 0 && log_failure) {
- cc_log("x_unlink failed: %s", strerror(saved_errno));
- }
- }
- free(tmp_name);
- errno = saved_errno;
- return result;
-}
-
-// Remove path, NFS safe, log both successes and failures.
-int
-x_unlink(const char* path)
-{
- return do_x_unlink(path, true);
-}
-
-// Remove path, NFS safe, only log successes.
-int
-x_try_unlink(const char* path)
-{
- return do_x_unlink(path, false);
-}
-
// Reads the content of a file. Size hint 0 means no hint. Returns true on
// success, otherwise false.
bool
void update_mtime(const char* path);
void x_exit(int status) ATTR_NORETURN;
int x_rename(const char* oldpath, const char* newpath);
-int tmp_unlink(const char* path);
-int x_unlink(const char* path);
-int x_try_unlink(const char* path);
bool read_file(const char* path, size_t size_hint, char** data, size_t* size);
char* read_text_file(const char* path, size_t size_hint);
char* subst_env_in_string(const char* str, char** errmsg);
cc_log("Failed to clone: %s", strerror(errno));
}
if (ctx.config.hard_link()) {
- x_try_unlink(dest.c_str());
+ unlink(dest.c_str());
cc_log("Hard linking %s to %s", source.c_str(), dest.c_str());
int ret = link(source.c_str(), dest.c_str());
if (ret == 0) {
stats_zero(const Config& config)
{
char* fname = format("%s/stats", config.cache_dir().c_str());
- x_unlink(fname);
+ Util::unlink_safe(fname);
free(fname);
time_t timestamp = time(nullptr);