diff --git a/base/cvd/cuttlefish/host/commands/cvd/fetch/BUILD.bazel b/base/cvd/cuttlefish/host/commands/cvd/fetch/BUILD.bazel index 687f06dc064..7028cb52427 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/fetch/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/cvd/fetch/BUILD.bazel @@ -325,6 +325,7 @@ cf_cc_library( "//libbase", "@abseil-cpp//absl/log", "@abseil-cpp//absl/strings", + "@fmt", "@protobuf", ], ) diff --git a/base/cvd/cuttlefish/host/commands/cvd/fetch/substitute.cc b/base/cvd/cuttlefish/host/commands/cvd/fetch/substitute.cc index 7f539120c51..35cf7d666b0 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/fetch/substitute.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/fetch/substitute.cc @@ -17,7 +17,9 @@ #include +#include #include +#include #include #include #include @@ -26,6 +28,7 @@ #include "absl/strings/match.h" #include "absl/strings/strip.h" #include "android-base/file.h" +#include "fmt/format.h" #include "google/protobuf/text_format.h" #include "cuttlefish/common/libs/utils/environment.h" @@ -63,10 +66,27 @@ Result Substitute(const std::string& target, CF_EXPECT(EnsureDirectoryExists(android::base::Dirname(full_link_name))); - int unlink_res = unlink(full_link_name.c_str()); - CF_EXPECTF(unlink_res == 0 || errno == ENOENT, "{}", StrError(errno)); + std::random_device rd; + std::string tmp_link_name; + constexpr int kMaxRetries = 10; + for (int attempt = 0; attempt < kMaxRetries; ++attempt) { + tmp_link_name = fmt::format("{}.tmp_{}_{}", full_link_name, getpid(), rd()); + auto symlink_res = Symlink(target, tmp_link_name); + if (symlink_res.has_value()) { + break; + } + if (errno == EEXIST && attempt + 1 < kMaxRetries) { + continue; + } + return symlink_res; + } + + if (rename(tmp_link_name.c_str(), full_link_name.c_str()) != 0) { + const int err = errno; + unlink(tmp_link_name.c_str()); + return CF_ERRF("Failed to rename symlink: {}", StrError(err)); + } - CF_EXPECT(Symlink(target, full_link_name)); return {}; }