Client: convert IFileTime to use Path
Benefits of a Path abstraction instead of raw
strings:
- type safety
- no need to convert paths on Windows all the time
- no need to check if paths are absolute
PiperOrigin-RevId: 266312309
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index a13acbd..fd2bb50 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -913,6 +913,8 @@
}
static void MoveFiles(const string &embedded_binaries) {
+ blaze_util::Path embedded_binaries_(embedded_binaries);
+
// Set the timestamps of the extracted files to the future and make sure (or
// at least as sure as we can...) that the files we have written are actually
// on the disk.
@@ -923,9 +925,9 @@
blaze_util::GetAllFilesUnder(embedded_binaries, &extracted_files);
std::unique_ptr<blaze_util::IFileMtime> mtime(blaze_util::CreateFileMtime());
- set<string> synced_directories;
- for (const auto &it : extracted_files) {
- const char *extracted_path = it.c_str();
+ set<blaze_util::Path> synced_directories;
+ for (const auto &f : extracted_files) {
+ blaze_util::Path it(f);
// Set the time to a distantly futuristic value so we can observe tampering.
// Note that keeping a static, deterministic timestamp, such as the default
@@ -935,14 +937,15 @@
// changed. This is essential for the correctness of actions that use
// embedded binaries as artifacts.
if (!mtime->SetToDistantFuture(it)) {
+ string err = GetLastErrorString();
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "failed to set timestamp on '" << extracted_path
- << "': " << GetLastErrorString();
+ << "failed to set timestamp on '" << it.AsPrintablePath()
+ << "': " << err;
}
blaze_util::SyncFile(it);
- string directory = blaze_util::Dirname(extracted_path);
+ blaze_util::Path directory = it.GetParent();
// Now walk up until embedded_binaries and sync every directory in between.
// synced_directories is used to avoid syncing the same directory twice.
@@ -950,16 +953,16 @@
// conditions are not strictly needed, but it makes this loop more robust,
// because otherwise, if due to some glitch, directory was not under
// embedded_binaries, it would get into an infinite loop.
- while (directory != embedded_binaries &&
- synced_directories.count(directory) == 0 && !directory.empty() &&
+ while (directory != embedded_binaries_ &&
+ synced_directories.count(directory) == 0 && !directory.IsEmpty() &&
!blaze_util::IsRootDirectory(directory)) {
blaze_util::SyncFile(directory);
synced_directories.insert(directory);
- directory = blaze_util::Dirname(directory);
+ directory = directory.GetParent();
}
}
- blaze_util::SyncFile(embedded_binaries);
+ blaze_util::SyncFile(embedded_binaries_);
}
@@ -980,8 +983,7 @@
// Work in a temp dir to avoid races.
string tmp_install = startup_options.install_base + ".tmp." +
blaze::GetProcessIdAsString();
- string tmp_binaries =
- blaze_util::JoinPath(tmp_install, "_embedded_binaries");
+ string tmp_binaries = GetEmbeddedBinariesRoot(tmp_install);
ExtractArchiveOrDie(
self_path,
startup_options.product_name,
@@ -1030,13 +1032,14 @@
std::unique_ptr<blaze_util::IFileMtime> mtime(
blaze_util::CreateFileMtime());
- string real_install_dir = blaze_util::JoinPath(
- startup_options.install_base, "_embedded_binaries");
+ blaze_util::Path real_install_dir =
+ blaze_util::Path(startup_options.install_base)
+ .GetRelative("_embedded_binaries");
for (const auto &it : archive_contents) {
- string path = blaze_util::JoinPath(real_install_dir, it);
+ blaze_util::Path path = real_install_dir.GetRelative(it);
if (!mtime->IsUntampered(path)) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "corrupt installation: file '" << path
+ << "corrupt installation: file '" << path.AsPrintablePath()
<< "' is missing or modified. Please remove '"
<< startup_options.install_base << "' and try again.";
}
@@ -1202,10 +1205,11 @@
// find install bases that haven't been used for a long time
std::unique_ptr<blaze_util::IFileMtime> mtime(
blaze_util::CreateFileMtime());
- if (!mtime->SetToNow(startup_options.install_base)) {
+ if (!mtime->SetToNow(blaze_util::Path(startup_options.install_base))) {
+ string err = GetLastErrorString();
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "failed to set timestamp on '" << startup_options.install_base
- << "': " << GetLastErrorString();
+ << "': " << err;
}
}
}
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h
index aa48029..bf462fd 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -42,17 +42,17 @@
// TODO(laszlocsomor): move this function, and with it the whole IFileMtime
// class into blaze_util_<platform>.cc, because it is Bazel-specific logic,
// not generic file-handling logic.
- virtual bool IsUntampered(const std::string &path) = 0;
+ virtual bool IsUntampered(const Path &path) = 0;
// Sets the mtime of file under `path` to the current time.
// Returns true if the mtime was changed successfully.
- virtual bool SetToNow(const std::string &path) = 0;
+ virtual bool SetToNow(const Path &path) = 0;
// Sets the mtime of file under `path` to the distant future.
// "Distant future" should be on the order of some years into the future, like
// a decade.
// Returns true if the mtime was changed successfully.
- virtual bool SetToDistantFuture(const std::string &path) = 0;
+ virtual bool SetToDistantFuture(const Path &path) = 0;
};
// Creates a platform-specific implementation of `IFileMtime`.
@@ -184,6 +184,7 @@
// Calls fsync() on the file (or directory) specified in 'file_path'.
// pdie() if syncing fails.
void SyncFile(const std::string& path);
+void SyncFile(const Path &path);
// mkdir -p path. All newly created directories use the given mode.
// `mode` should be an octal permission mask, e.g. 0755.
diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc
index 57c3fbe..c225f9c 100644
--- a/src/main/cpp/util/file_posix.cc
+++ b/src/main/cpp/util/file_posix.cc
@@ -358,15 +358,17 @@
close(fd);
}
+void SyncFile(const Path &path) { SyncFile(path.AsNativePath()); }
+
class PosixFileMtime : public IFileMtime {
public:
PosixFileMtime()
: near_future_(GetFuture(9)),
distant_future_({GetFuture(10), GetFuture(10)}) {}
- bool IsUntampered(const string &path) override;
- bool SetToNow(const string &path) override;
- bool SetToDistantFuture(const string &path) override;
+ bool IsUntampered(const Path &path) override;
+ bool SetToNow(const Path &path) override;
+ bool SetToDistantFuture(const Path &path) override;
private:
// 9 years in the future.
@@ -374,14 +376,14 @@
// 10 years in the future.
const struct utimbuf distant_future_;
- static bool Set(const string &path, const struct utimbuf &mtime);
+ static bool Set(const Path &path, const struct utimbuf &mtime);
static time_t GetNow();
static time_t GetFuture(unsigned int years);
};
-bool PosixFileMtime::IsUntampered(const string &path) {
+bool PosixFileMtime::IsUntampered(const Path &path) {
struct stat buf;
- if (stat(path.c_str(), &buf)) {
+ if (stat(path.AsNativePath().c_str(), &buf)) {
return false;
}
@@ -393,18 +395,18 @@
return S_ISDIR(buf.st_mode) || (buf.st_mtime > near_future_);
}
-bool PosixFileMtime::SetToNow(const string &path) {
+bool PosixFileMtime::SetToNow(const Path &path) {
time_t now(GetNow());
struct utimbuf times = {now, now};
return Set(path, times);
}
-bool PosixFileMtime::SetToDistantFuture(const string &path) {
+bool PosixFileMtime::SetToDistantFuture(const Path &path) {
return Set(path, distant_future_);
}
-bool PosixFileMtime::Set(const string &path, const struct utimbuf &mtime) {
- return utime(path.c_str(), &mtime) == 0;
+bool PosixFileMtime::Set(const Path &path, const struct utimbuf &mtime) {
+ return utime(path.AsNativePath().c_str(), &mtime) == 0;
}
time_t PosixFileMtime::GetNow() {
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index faf146d..e14434e 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -111,9 +111,9 @@
WindowsFileMtime()
: near_future_(GetFuture(9)), distant_future_(GetFuture(10)) {}
- bool IsUntampered(const string& path) override;
- bool SetToNow(const string& path) override;
- bool SetToDistantFuture(const string& path) override;
+ bool IsUntampered(const Path& path) override;
+ bool SetToNow(const Path& path) override;
+ bool SetToDistantFuture(const Path& path) override;
private:
// 9 years in the future.
@@ -123,32 +123,24 @@
static FILETIME GetNow();
static FILETIME GetFuture(WORD years);
- static bool Set(const string& path, FILETIME time);
+ static bool Set(const Path& path, FILETIME time);
};
-bool WindowsFileMtime::IsUntampered(const string& path) {
- if (path.empty() || IsDevNull(path.c_str())) {
+bool WindowsFileMtime::IsUntampered(const Path& path) {
+ if (path.IsEmpty() || path.IsNull()) {
return false;
}
- wstring wpath;
- string error;
- if (!AsAbsoluteWindowsPath(path, &wpath, &error)) {
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "WindowsFileMtime::IsUntampered(" << path
- << "): AsAbsoluteWindowsPath failed: " << error;
- }
-
// Get attributes, to check if the file exists. (It may still be a dangling
// junction.)
- DWORD attrs = GetFileAttributesW(wpath.c_str());
+ DWORD attrs = GetFileAttributesW(path.AsNativePath().c_str());
if (attrs == INVALID_FILE_ATTRIBUTES) {
return false;
}
bool is_directory = attrs & FILE_ATTRIBUTE_DIRECTORY;
AutoHandle handle(CreateFileW(
- /* lpFileName */ wpath.c_str(),
+ /* lpFileName */ path.AsNativePath().c_str(),
/* dwDesiredAccess */ GENERIC_READ,
/* dwShareMode */ FILE_SHARE_READ,
/* lpSecurityAttributes */ NULL,
@@ -181,35 +173,23 @@
}
}
-bool WindowsFileMtime::SetToNow(const string& path) {
+bool WindowsFileMtime::SetToNow(const Path& path) {
return Set(path, GetNow());
}
-bool WindowsFileMtime::SetToDistantFuture(const string& path) {
+bool WindowsFileMtime::SetToDistantFuture(const Path& path) {
return Set(path, distant_future_);
}
-bool WindowsFileMtime::Set(const string& path, FILETIME time) {
- if (path.empty()) {
- return false;
- }
- wstring wpath;
- string error;
- if (!AsAbsoluteWindowsPath(path, &wpath, &error)) {
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "WindowsFileMtime::Set(" << path
- << "): AsAbsoluteWindowsPath failed: " << error;
- return false;
- }
-
+bool WindowsFileMtime::Set(const Path& path, FILETIME time) {
AutoHandle handle(::CreateFileW(
- /* lpFileName */ wpath.c_str(),
+ /* lpFileName */ path.AsNativePath().c_str(),
/* dwDesiredAccess */ FILE_WRITE_ATTRIBUTES,
/* dwShareMode */ FILE_SHARE_READ,
/* lpSecurityAttributes */ NULL,
/* dwCreationDisposition */ OPEN_EXISTING,
/* dwFlagsAndAttributes */
- IsDirectoryW(wpath)
+ IsDirectoryW(path.AsNativePath())
? (FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)
: FILE_ATTRIBUTE_NORMAL,
/* hTemplateFile */ NULL));
@@ -610,6 +590,8 @@
// fsync always fails on Cygwin with "Permission denied" for some reason.
}
+void SyncFile(const Path& path) {}
+
bool MakeDirectoriesW(const wstring& path, unsigned int mode) {
if (path.empty()) {
return false;
diff --git a/src/main/cpp/util/path_platform.h b/src/main/cpp/util/path_platform.h
index bc23192..bfa448b 100644
--- a/src/main/cpp/util/path_platform.h
+++ b/src/main/cpp/util/path_platform.h
@@ -27,13 +27,14 @@
explicit Path(const std::string &path);
bool operator==(const Path &o) const { return path_ == o.path_; }
bool operator!=(const Path &o) const { return path_ != o.path_; }
+ bool operator<(const Path &o) const { return path_ < o.path_; }
bool IsEmpty() const { return path_.empty(); }
bool IsNull() const;
bool Contains(const char c) const;
bool Contains(const std::string &s) const;
Path GetRelative(const std::string &r) const;
- // Returns the canonical form (like realpath(2)) of this path.
+ // Returns the canonical form (like realpath(1)) of this path.
// All symlinks in the path are resolved.
// If canonicalization fails, returns an empty Path.
Path Canonicalize() const;
@@ -95,6 +96,7 @@
// Returns true if `path` is the root directory or a Windows drive root.
bool IsRootDirectory(const std::string &path);
+bool IsRootDirectory(const Path &path);
// Returns true if `path` is absolute.
bool IsAbsolute(const std::string &path);
diff --git a/src/main/cpp/util/path_posix.cc b/src/main/cpp/util/path_posix.cc
index 9c44d0e..a5be228 100644
--- a/src/main/cpp/util/path_posix.cc
+++ b/src/main/cpp/util/path_posix.cc
@@ -56,6 +56,10 @@
return path.size() == 1 && path[0] == '/';
}
+bool IsRootDirectory(const Path &path) {
+ return IsRootDirectory(path.AsNativePath());
+}
+
bool IsAbsolute(const std::string &path) {
return !path.empty() && path[0] == '/';
}
diff --git a/src/main/cpp/util/path_windows.cc b/src/main/cpp/util/path_windows.cc
index 7ebf2a8..a9f9780 100644
--- a/src/main/cpp/util/path_windows.cc
+++ b/src/main/cpp/util/path_windows.cc
@@ -449,6 +449,10 @@
return IsRootOrAbsolute(path, true);
}
+bool IsRootDirectory(const Path& path) {
+ return IsRootOrAbsolute(path.AsNativePath(), true);
+}
+
bool IsAbsolute(const std::string& path) {
return IsRootOrAbsolute(path, false);
}
diff --git a/src/test/cpp/util/file_test.cc b/src/test/cpp/util/file_test.cc
index eeb1e2c..178ff48 100644
--- a/src/test/cpp/util/file_test.cc
+++ b/src/test/cpp/util/file_test.cc
@@ -145,14 +145,14 @@
const char* tempdir_cstr = getenv("TEST_TMPDIR");
ASSERT_NE(tempdir_cstr, nullptr);
ASSERT_NE(tempdir_cstr[0], 0);
- string tempdir(tempdir_cstr);
+ Path tempdir(tempdir_cstr);
std::unique_ptr<IFileMtime> mtime(CreateFileMtime());
// Assert that a directory is always untampered with. (We do
// not care about directories' mtimes.)
ASSERT_TRUE(mtime->IsUntampered(tempdir));
// Create a new file, assert its mtime is not in the future.
- string file(JoinPath(tempdir, "foo.txt"));
+ Path file = tempdir.GetRelative("foo.txt");
ASSERT_TRUE(WriteFile("hello", 5, file));
ASSERT_FALSE(mtime->IsUntampered(file));
// Set the file's mtime to the future, assert that it's so.
diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc
index a86d664..83ae4f6 100644
--- a/src/test/cpp/util/file_windows_test.cc
+++ b/src/test/cpp/util/file_windows_test.cc
@@ -320,24 +320,23 @@
const char* tempdir_cstr = getenv("TEST_TMPDIR");
ASSERT_NE(tempdir_cstr, nullptr);
ASSERT_NE(tempdir_cstr[0], 0);
- string tempdir(tempdir_cstr);
+ Path tempdir(tempdir_cstr);
- string target(JoinPath(tempdir, "target" TOSTRING(__LINE__)));
- wstring wtarget;
- EXPECT_TRUE(AsWindowsPath(target, &wtarget, nullptr));
- EXPECT_TRUE(CreateDirectoryW(wtarget.c_str(), NULL));
+ Path target = tempdir.GetRelative("target" TOSTRING(__LINE__));
+ EXPECT_TRUE(CreateDirectoryW(target.AsNativePath().c_str(), NULL));
std::unique_ptr<IFileMtime> mtime(CreateFileMtime());
// Assert that a directory is always a good embedded binary. (We do not care
// about directories' mtimes.)
ASSERT_TRUE(mtime.get()->IsUntampered(target));
// Assert that junctions whose target exists are "good" embedded binaries.
- string sym(JoinPath(tempdir, "junc" TOSTRING(__LINE__)));
- CREATE_JUNCTION(sym, target);
+ Path sym = tempdir.GetRelative("junc" TOSTRING(__LINE__));
+ EXPECT_EQ(CreateJunction(sym.AsNativePath(), target.AsNativePath(), nullptr),
+ CreateJunctionResult::kSuccess);
ASSERT_TRUE(mtime.get()->IsUntampered(sym));
// Assert that checking fails for non-existent directories and dangling
// junctions.
- EXPECT_TRUE(RemoveDirectoryW(wtarget.c_str()));
+ EXPECT_TRUE(RemoveDirectoryW(target.AsNativePath().c_str()));
ASSERT_FALSE(mtime.get()->IsUntampered(target));
ASSERT_FALSE(mtime.get()->IsUntampered(sym));
}