diff options
Diffstat (limited to 'common/libs/utils/files.cpp')
-rw-r--r-- | common/libs/utils/files.cpp | 117 |
1 files changed, 115 insertions, 2 deletions
diff --git a/common/libs/utils/files.cpp b/common/libs/utils/files.cpp index 5fa1e6ecf..095674af6 100644 --- a/common/libs/utils/files.cpp +++ b/common/libs/utils/files.cpp @@ -52,6 +52,7 @@ #include <numeric> #include <ostream> #include <ratio> +#include <regex> #include <string> #include <vector> @@ -81,6 +82,66 @@ bool FileExists(const std::string& path, bool follow_symlinks) { return (follow_symlinks ? stat : lstat)(path.c_str(), &st) == 0; } +Result<dev_t> FileDeviceId(const std::string& path) { + struct stat out; + CF_EXPECTF( + stat(path.c_str(), &out) == 0, + "stat() failed trying to retrieve device ID information for \"{}\" " + "with error: {}", + path, strerror(errno)); + return out.st_dev; +} + +Result<bool> CanHardLink(const std::string& source, + const std::string& destination) { + return CF_EXPECT(FileDeviceId(source)) == + CF_EXPECT(FileDeviceId(destination)); +} + +Result<ino_t> FileInodeNumber(const std::string& path) { + struct stat out; + CF_EXPECTF( + stat(path.c_str(), &out) == 0, + "stat() failed trying to retrieve inode num information for \"{}\" " + "with error: {}", + path, strerror(errno)); + return out.st_ino; +} + +Result<bool> AreHardLinked(const std::string& source, + const std::string& destination) { + return (CF_EXPECT(FileDeviceId(source)) == + CF_EXPECT(FileDeviceId(destination))) && + (CF_EXPECT(FileInodeNumber(source)) == + CF_EXPECT(FileInodeNumber(destination))); +} + +Result<std::string> CreateHardLink(const std::string& target, + const std::string& hardlink, + const bool overwrite_existing) { + if (FileExists(hardlink)) { + if (CF_EXPECT(AreHardLinked(target, hardlink))) { + return hardlink; + } + if (!overwrite_existing) { + return CF_ERRF( + "Cannot hardlink from \"{}\" to \"{}\", the second file already " + "exists and is not hardlinked to the first", + target, hardlink); + } + LOG(WARNING) << "Overwriting existing file \"" << hardlink << "\" with \"" + << target << "\" from the cache"; + CF_EXPECTF(unlink(hardlink.c_str()) == 0, + "Failed to unlink \"{}\" with error: {}", hardlink, + strerror(errno)); + } + CF_EXPECTF(link(target.c_str(), hardlink.c_str()) == 0, + "link() failed trying to create hardlink from \"{}\" to \"{}\" " + "with error: {}", + target, hardlink, strerror(errno)); + return hardlink; +} + bool FileHasContent(const std::string& path) { return FileSize(path) > 0; } @@ -129,8 +190,12 @@ Result<void> EnsureDirectoryExists(const std::string& directory_path, << strerror(errno)); } + CF_EXPECTF(chmod(directory_path.c_str(), mode) == 0, + "Failed to set permission on {}: {}", directory_path, + strerror(errno)); + if (group_name != "") { - ChangeGroup(directory_path, group_name); + CF_EXPECT(ChangeGroup(directory_path, group_name)); } return {}; @@ -145,7 +210,7 @@ Result<void> ChangeGroup(const std::string& path, } if (chown(path.c_str(), -1, groupId) != 0) { - return CF_ERRNO("Feailed to set group for path: " + return CF_ERRNO("Failed to set group for path: " << path << ", " << group_name << ", " << strerror(errno)); } @@ -663,6 +728,54 @@ Result<void> WaitForUnixSocket(const std::string& path, int timeoutSec) { return CF_ERR("This shouldn't be executed"); } + +Result<void> WaitForUnixSocketListeningWithoutConnect(const std::string& path, + int timeoutSec) { + const auto targetTime = + std::chrono::system_clock::now() + std::chrono::seconds(timeoutSec); + + CF_EXPECT(WaitForFile(path, timeoutSec), + "Waiting for socket path creation failed"); + CF_EXPECT(FileIsSocket(path), "Specified path is not a socket"); + + std::regex socket_state_regex("TST=(.*)"); + + while (true) { + const auto currentTime = std::chrono::system_clock::now(); + + if (currentTime >= targetTime) { + return CF_ERR("Timed out"); + } + + Command lsof("lsof"); + lsof.AddParameter(/*"format"*/ "-F", /*"connection state"*/ "TST"); + lsof.AddParameter(path); + std::string lsof_out; + std::string lsof_err; + int rval = + RunWithManagedStdio(std::move(lsof), nullptr, &lsof_out, &lsof_err); + if (rval != 0) { + return CF_ERR("Failed to run `lsof`, stderr: " << lsof_err); + } + + LOG(DEBUG) << "lsof stdout:|" << lsof_out << "|"; + LOG(DEBUG) << "lsof stderr:|" << lsof_err << "|"; + + std::smatch socket_state_match; + if (std::regex_search(lsof_out, socket_state_match, socket_state_regex)) { + if (socket_state_match.size() == 2) { + const std::string& socket_state = socket_state_match[1]; + if (socket_state == "LISTEN") { + return {}; + } + } + } + + sched_yield(); + } + + return CF_ERR("This shouldn't be executed"); +} #endif namespace { |