1695b41eeSopenharmony_ci// Copyright 2011 Google Inc. All Rights Reserved. 2695b41eeSopenharmony_ci// 3695b41eeSopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 4695b41eeSopenharmony_ci// you may not use this file except in compliance with the License. 5695b41eeSopenharmony_ci// You may obtain a copy of the License at 6695b41eeSopenharmony_ci// 7695b41eeSopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 8695b41eeSopenharmony_ci// 9695b41eeSopenharmony_ci// Unless required by applicable law or agreed to in writing, software 10695b41eeSopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 11695b41eeSopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12695b41eeSopenharmony_ci// See the License for the specific language governing permissions and 13695b41eeSopenharmony_ci// limitations under the License. 14695b41eeSopenharmony_ci 15695b41eeSopenharmony_ci#include "disk_interface.h" 16695b41eeSopenharmony_ci 17695b41eeSopenharmony_ci#include <algorithm> 18695b41eeSopenharmony_ci 19695b41eeSopenharmony_ci#include <errno.h> 20695b41eeSopenharmony_ci#include <stdio.h> 21695b41eeSopenharmony_ci#include <string.h> 22695b41eeSopenharmony_ci#include <sys/stat.h> 23695b41eeSopenharmony_ci#include <sys/types.h> 24695b41eeSopenharmony_ci 25695b41eeSopenharmony_ci#ifdef _WIN32 26695b41eeSopenharmony_ci#include <direct.h> // _mkdir 27695b41eeSopenharmony_ci#include <windows.h> 28695b41eeSopenharmony_ci 29695b41eeSopenharmony_ci#include <sstream> 30695b41eeSopenharmony_ci#else 31695b41eeSopenharmony_ci#include <unistd.h> 32695b41eeSopenharmony_ci#endif 33695b41eeSopenharmony_ci 34695b41eeSopenharmony_ci#include "metrics.h" 35695b41eeSopenharmony_ci#include "util.h" 36695b41eeSopenharmony_ci 37695b41eeSopenharmony_ciusing namespace std; 38695b41eeSopenharmony_ci 39695b41eeSopenharmony_cinamespace { 40695b41eeSopenharmony_ci 41695b41eeSopenharmony_cistring DirName(const string& path) { 42695b41eeSopenharmony_ci#ifdef _WIN32 43695b41eeSopenharmony_ci static const char kPathSeparators[] = "\\/"; 44695b41eeSopenharmony_ci#else 45695b41eeSopenharmony_ci static const char kPathSeparators[] = "/"; 46695b41eeSopenharmony_ci#endif 47695b41eeSopenharmony_ci static const char* const kEnd = kPathSeparators + sizeof(kPathSeparators) - 1; 48695b41eeSopenharmony_ci 49695b41eeSopenharmony_ci string::size_type slash_pos = path.find_last_of(kPathSeparators); 50695b41eeSopenharmony_ci if (slash_pos == string::npos) 51695b41eeSopenharmony_ci return string(); // Nothing to do. 52695b41eeSopenharmony_ci while (slash_pos > 0 && 53695b41eeSopenharmony_ci std::find(kPathSeparators, kEnd, path[slash_pos - 1]) != kEnd) 54695b41eeSopenharmony_ci --slash_pos; 55695b41eeSopenharmony_ci return path.substr(0, slash_pos); 56695b41eeSopenharmony_ci} 57695b41eeSopenharmony_ci 58695b41eeSopenharmony_ciint MakeDir(const string& path) { 59695b41eeSopenharmony_ci#ifdef _WIN32 60695b41eeSopenharmony_ci return _mkdir(path.c_str()); 61695b41eeSopenharmony_ci#else 62695b41eeSopenharmony_ci return mkdir(path.c_str(), 0777); 63695b41eeSopenharmony_ci#endif 64695b41eeSopenharmony_ci} 65695b41eeSopenharmony_ci 66695b41eeSopenharmony_ci#ifdef _WIN32 67695b41eeSopenharmony_ciTimeStamp TimeStampFromFileTime(const FILETIME& filetime) { 68695b41eeSopenharmony_ci // FILETIME is in 100-nanosecond increments since the Windows epoch. 69695b41eeSopenharmony_ci // We don't much care about epoch correctness but we do want the 70695b41eeSopenharmony_ci // resulting value to fit in a 64-bit integer. 71695b41eeSopenharmony_ci uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | 72695b41eeSopenharmony_ci ((uint64_t)filetime.dwLowDateTime); 73695b41eeSopenharmony_ci // 1600 epoch -> 2000 epoch (subtract 400 years). 74695b41eeSopenharmony_ci return (TimeStamp)mtime - 12622770400LL * (1000000000LL / 100); 75695b41eeSopenharmony_ci} 76695b41eeSopenharmony_ci 77695b41eeSopenharmony_ciTimeStamp StatSingleFile(const string& path, string* err) { 78695b41eeSopenharmony_ci WIN32_FILE_ATTRIBUTE_DATA attrs; 79695b41eeSopenharmony_ci if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) { 80695b41eeSopenharmony_ci DWORD win_err = GetLastError(); 81695b41eeSopenharmony_ci if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) 82695b41eeSopenharmony_ci return 0; 83695b41eeSopenharmony_ci *err = "GetFileAttributesEx(" + path + "): " + GetLastErrorString(); 84695b41eeSopenharmony_ci return -1; 85695b41eeSopenharmony_ci } 86695b41eeSopenharmony_ci return TimeStampFromFileTime(attrs.ftLastWriteTime); 87695b41eeSopenharmony_ci} 88695b41eeSopenharmony_ci 89695b41eeSopenharmony_cibool IsWindows7OrLater() { 90695b41eeSopenharmony_ci OSVERSIONINFOEX version_info = 91695b41eeSopenharmony_ci { sizeof(OSVERSIONINFOEX), 6, 1, 0, 0, {0}, 0, 0, 0, 0, 0}; 92695b41eeSopenharmony_ci DWORDLONG comparison = 0; 93695b41eeSopenharmony_ci VER_SET_CONDITION(comparison, VER_MAJORVERSION, VER_GREATER_EQUAL); 94695b41eeSopenharmony_ci VER_SET_CONDITION(comparison, VER_MINORVERSION, VER_GREATER_EQUAL); 95695b41eeSopenharmony_ci return VerifyVersionInfo( 96695b41eeSopenharmony_ci &version_info, VER_MAJORVERSION | VER_MINORVERSION, comparison); 97695b41eeSopenharmony_ci} 98695b41eeSopenharmony_ci 99695b41eeSopenharmony_cibool StatAllFilesInDir(const string& dir, map<string, TimeStamp>* stamps, 100695b41eeSopenharmony_ci string* err) { 101695b41eeSopenharmony_ci // FindExInfoBasic is 30% faster than FindExInfoStandard. 102695b41eeSopenharmony_ci static bool can_use_basic_info = IsWindows7OrLater(); 103695b41eeSopenharmony_ci // This is not in earlier SDKs. 104695b41eeSopenharmony_ci const FINDEX_INFO_LEVELS kFindExInfoBasic = 105695b41eeSopenharmony_ci static_cast<FINDEX_INFO_LEVELS>(1); 106695b41eeSopenharmony_ci FINDEX_INFO_LEVELS level = 107695b41eeSopenharmony_ci can_use_basic_info ? kFindExInfoBasic : FindExInfoStandard; 108695b41eeSopenharmony_ci WIN32_FIND_DATAA ffd; 109695b41eeSopenharmony_ci HANDLE find_handle = FindFirstFileExA((dir + "\\*").c_str(), level, &ffd, 110695b41eeSopenharmony_ci FindExSearchNameMatch, NULL, 0); 111695b41eeSopenharmony_ci 112695b41eeSopenharmony_ci if (find_handle == INVALID_HANDLE_VALUE) { 113695b41eeSopenharmony_ci DWORD win_err = GetLastError(); 114695b41eeSopenharmony_ci if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND || 115695b41eeSopenharmony_ci win_err == ERROR_DIRECTORY) 116695b41eeSopenharmony_ci return true; 117695b41eeSopenharmony_ci *err = "FindFirstFileExA(" + dir + "): " + GetLastErrorString(); 118695b41eeSopenharmony_ci return false; 119695b41eeSopenharmony_ci } 120695b41eeSopenharmony_ci do { 121695b41eeSopenharmony_ci string lowername = ffd.cFileName; 122695b41eeSopenharmony_ci if (lowername == "..") { 123695b41eeSopenharmony_ci // Seems to just copy the timestamp for ".." from ".", which is wrong. 124695b41eeSopenharmony_ci // This is the case at least on NTFS under Windows 7. 125695b41eeSopenharmony_ci continue; 126695b41eeSopenharmony_ci } 127695b41eeSopenharmony_ci transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower); 128695b41eeSopenharmony_ci stamps->insert(make_pair(lowername, 129695b41eeSopenharmony_ci TimeStampFromFileTime(ffd.ftLastWriteTime))); 130695b41eeSopenharmony_ci } while (FindNextFileA(find_handle, &ffd)); 131695b41eeSopenharmony_ci FindClose(find_handle); 132695b41eeSopenharmony_ci return true; 133695b41eeSopenharmony_ci} 134695b41eeSopenharmony_ci#endif // _WIN32 135695b41eeSopenharmony_ci 136695b41eeSopenharmony_ci} // namespace 137695b41eeSopenharmony_ci 138695b41eeSopenharmony_ci// DiskInterface --------------------------------------------------------------- 139695b41eeSopenharmony_ci 140695b41eeSopenharmony_cibool DiskInterface::MakeDirs(const string& path) { 141695b41eeSopenharmony_ci string dir = DirName(path); 142695b41eeSopenharmony_ci if (dir.empty()) 143695b41eeSopenharmony_ci return true; // Reached root; assume it's there. 144695b41eeSopenharmony_ci string err; 145695b41eeSopenharmony_ci TimeStamp mtime = Stat(dir, &err); 146695b41eeSopenharmony_ci if (mtime < 0) { 147695b41eeSopenharmony_ci Error("%s", err.c_str()); 148695b41eeSopenharmony_ci return false; 149695b41eeSopenharmony_ci } 150695b41eeSopenharmony_ci if (mtime > 0) 151695b41eeSopenharmony_ci return true; // Exists already; we're done. 152695b41eeSopenharmony_ci 153695b41eeSopenharmony_ci // Directory doesn't exist. Try creating its parent first. 154695b41eeSopenharmony_ci bool success = MakeDirs(dir); 155695b41eeSopenharmony_ci if (!success) 156695b41eeSopenharmony_ci return false; 157695b41eeSopenharmony_ci return MakeDir(dir); 158695b41eeSopenharmony_ci} 159695b41eeSopenharmony_ci 160695b41eeSopenharmony_ci// RealDiskInterface ----------------------------------------------------------- 161695b41eeSopenharmony_ciRealDiskInterface::RealDiskInterface() 162695b41eeSopenharmony_ci#ifdef _WIN32 163695b41eeSopenharmony_ci: use_cache_(false), long_paths_enabled_(false) { 164695b41eeSopenharmony_ci setlocale(LC_ALL, ""); 165695b41eeSopenharmony_ci 166695b41eeSopenharmony_ci // Probe ntdll.dll for RtlAreLongPathsEnabled, and call it if it exists. 167695b41eeSopenharmony_ci HINSTANCE ntdll_lib = ::GetModuleHandleW(L"ntdll"); 168695b41eeSopenharmony_ci if (ntdll_lib) { 169695b41eeSopenharmony_ci typedef BOOLEAN(WINAPI FunctionType)(); 170695b41eeSopenharmony_ci auto* func_ptr = reinterpret_cast<FunctionType*>( 171695b41eeSopenharmony_ci ::GetProcAddress(ntdll_lib, "RtlAreLongPathsEnabled")); 172695b41eeSopenharmony_ci if (func_ptr) { 173695b41eeSopenharmony_ci long_paths_enabled_ = (*func_ptr)(); 174695b41eeSopenharmony_ci } 175695b41eeSopenharmony_ci } 176695b41eeSopenharmony_ci} 177695b41eeSopenharmony_ci#else 178695b41eeSopenharmony_ci{} 179695b41eeSopenharmony_ci#endif 180695b41eeSopenharmony_ci 181695b41eeSopenharmony_ciTimeStamp RealDiskInterface::Stat(const string& path, string* err) const { 182695b41eeSopenharmony_ci METRIC_RECORD("node stat"); 183695b41eeSopenharmony_ci#ifdef _WIN32 184695b41eeSopenharmony_ci // MSDN: "Naming Files, Paths, and Namespaces" 185695b41eeSopenharmony_ci // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx 186695b41eeSopenharmony_ci if (!path.empty() && !AreLongPathsEnabled() && path[0] != '\\' && 187695b41eeSopenharmony_ci path.size() > MAX_PATH) { 188695b41eeSopenharmony_ci ostringstream err_stream; 189695b41eeSopenharmony_ci err_stream << "Stat(" << path << "): Filename longer than " << MAX_PATH 190695b41eeSopenharmony_ci << " characters"; 191695b41eeSopenharmony_ci *err = err_stream.str(); 192695b41eeSopenharmony_ci return -1; 193695b41eeSopenharmony_ci } 194695b41eeSopenharmony_ci if (!use_cache_) 195695b41eeSopenharmony_ci return StatSingleFile(path, err); 196695b41eeSopenharmony_ci 197695b41eeSopenharmony_ci string dir = DirName(path); 198695b41eeSopenharmony_ci string base(path.substr(dir.size() ? dir.size() + 1 : 0)); 199695b41eeSopenharmony_ci if (base == "..") { 200695b41eeSopenharmony_ci // StatAllFilesInDir does not report any information for base = "..". 201695b41eeSopenharmony_ci base = "."; 202695b41eeSopenharmony_ci dir = path; 203695b41eeSopenharmony_ci } 204695b41eeSopenharmony_ci 205695b41eeSopenharmony_ci string dir_lowercase = dir; 206695b41eeSopenharmony_ci transform(dir.begin(), dir.end(), dir_lowercase.begin(), ::tolower); 207695b41eeSopenharmony_ci transform(base.begin(), base.end(), base.begin(), ::tolower); 208695b41eeSopenharmony_ci 209695b41eeSopenharmony_ci Cache::iterator ci = cache_.find(dir_lowercase); 210695b41eeSopenharmony_ci if (ci == cache_.end()) { 211695b41eeSopenharmony_ci ci = cache_.insert(make_pair(dir_lowercase, DirCache())).first; 212695b41eeSopenharmony_ci if (!StatAllFilesInDir(dir.empty() ? "." : dir, &ci->second, err)) { 213695b41eeSopenharmony_ci cache_.erase(ci); 214695b41eeSopenharmony_ci return -1; 215695b41eeSopenharmony_ci } 216695b41eeSopenharmony_ci } 217695b41eeSopenharmony_ci DirCache::iterator di = ci->second.find(base); 218695b41eeSopenharmony_ci return di != ci->second.end() ? di->second : 0; 219695b41eeSopenharmony_ci#else 220695b41eeSopenharmony_ci#ifdef __USE_LARGEFILE64 221695b41eeSopenharmony_ci struct stat64 st; 222695b41eeSopenharmony_ci if (stat64(path.c_str(), &st) < 0) { 223695b41eeSopenharmony_ci#else 224695b41eeSopenharmony_ci struct stat st; 225695b41eeSopenharmony_ci if (stat(path.c_str(), &st) < 0) { 226695b41eeSopenharmony_ci#endif 227695b41eeSopenharmony_ci if (errno == ENOENT || errno == ENOTDIR) 228695b41eeSopenharmony_ci return 0; 229695b41eeSopenharmony_ci *err = "stat(" + path + "): " + strerror(errno); 230695b41eeSopenharmony_ci return -1; 231695b41eeSopenharmony_ci } 232695b41eeSopenharmony_ci // Some users (Flatpak) set mtime to 0, this should be harmless 233695b41eeSopenharmony_ci // and avoids conflicting with our return value of 0 meaning 234695b41eeSopenharmony_ci // that it doesn't exist. 235695b41eeSopenharmony_ci if (st.st_mtime == 0) 236695b41eeSopenharmony_ci return 1; 237695b41eeSopenharmony_ci#if defined(_AIX) 238695b41eeSopenharmony_ci return (int64_t)st.st_mtime * 1000000000LL + st.st_mtime_n; 239695b41eeSopenharmony_ci#elif defined(__APPLE__) 240695b41eeSopenharmony_ci return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL + 241695b41eeSopenharmony_ci st.st_mtimespec.tv_nsec); 242695b41eeSopenharmony_ci#elif defined(st_mtime) // A macro, so we're likely on modern POSIX. 243695b41eeSopenharmony_ci return (int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec; 244695b41eeSopenharmony_ci#else 245695b41eeSopenharmony_ci return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec; 246695b41eeSopenharmony_ci#endif 247695b41eeSopenharmony_ci#endif 248695b41eeSopenharmony_ci} 249695b41eeSopenharmony_ci 250695b41eeSopenharmony_cibool RealDiskInterface::WriteFile(const string& path, const string& contents) { 251695b41eeSopenharmony_ci FILE* fp = fopen(path.c_str(), "w"); 252695b41eeSopenharmony_ci if (fp == NULL) { 253695b41eeSopenharmony_ci Error("WriteFile(%s): Unable to create file. %s", 254695b41eeSopenharmony_ci path.c_str(), strerror(errno)); 255695b41eeSopenharmony_ci return false; 256695b41eeSopenharmony_ci } 257695b41eeSopenharmony_ci 258695b41eeSopenharmony_ci if (fwrite(contents.data(), 1, contents.length(), fp) < contents.length()) { 259695b41eeSopenharmony_ci Error("WriteFile(%s): Unable to write to the file. %s", 260695b41eeSopenharmony_ci path.c_str(), strerror(errno)); 261695b41eeSopenharmony_ci fclose(fp); 262695b41eeSopenharmony_ci return false; 263695b41eeSopenharmony_ci } 264695b41eeSopenharmony_ci 265695b41eeSopenharmony_ci if (fclose(fp) == EOF) { 266695b41eeSopenharmony_ci Error("WriteFile(%s): Unable to close the file. %s", 267695b41eeSopenharmony_ci path.c_str(), strerror(errno)); 268695b41eeSopenharmony_ci return false; 269695b41eeSopenharmony_ci } 270695b41eeSopenharmony_ci 271695b41eeSopenharmony_ci return true; 272695b41eeSopenharmony_ci} 273695b41eeSopenharmony_ci 274695b41eeSopenharmony_cibool RealDiskInterface::MakeDir(const string& path) { 275695b41eeSopenharmony_ci if (::MakeDir(path) < 0) { 276695b41eeSopenharmony_ci if (errno == EEXIST) { 277695b41eeSopenharmony_ci return true; 278695b41eeSopenharmony_ci } 279695b41eeSopenharmony_ci Error("mkdir(%s): %s", path.c_str(), strerror(errno)); 280695b41eeSopenharmony_ci return false; 281695b41eeSopenharmony_ci } 282695b41eeSopenharmony_ci return true; 283695b41eeSopenharmony_ci} 284695b41eeSopenharmony_ci 285695b41eeSopenharmony_ciFileReader::Status RealDiskInterface::ReadFile(const string& path, 286695b41eeSopenharmony_ci string* contents, 287695b41eeSopenharmony_ci string* err) { 288695b41eeSopenharmony_ci switch (::ReadFile(path, contents, err)) { 289695b41eeSopenharmony_ci case 0: return Okay; 290695b41eeSopenharmony_ci case -ENOENT: return NotFound; 291695b41eeSopenharmony_ci default: return OtherError; 292695b41eeSopenharmony_ci } 293695b41eeSopenharmony_ci} 294695b41eeSopenharmony_ci 295695b41eeSopenharmony_ciint RealDiskInterface::RemoveFile(const string& path) { 296695b41eeSopenharmony_ci#ifdef _WIN32 297695b41eeSopenharmony_ci DWORD attributes = GetFileAttributesA(path.c_str()); 298695b41eeSopenharmony_ci if (attributes == INVALID_FILE_ATTRIBUTES) { 299695b41eeSopenharmony_ci DWORD win_err = GetLastError(); 300695b41eeSopenharmony_ci if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) { 301695b41eeSopenharmony_ci return 1; 302695b41eeSopenharmony_ci } 303695b41eeSopenharmony_ci } else if (attributes & FILE_ATTRIBUTE_READONLY) { 304695b41eeSopenharmony_ci // On non-Windows systems, remove() will happily delete read-only files. 305695b41eeSopenharmony_ci // On Windows Ninja should behave the same: 306695b41eeSopenharmony_ci // https://github.com/ninja-build/ninja/issues/1886 307695b41eeSopenharmony_ci // Skip error checking. If this fails, accept whatever happens below. 308695b41eeSopenharmony_ci SetFileAttributesA(path.c_str(), attributes & ~FILE_ATTRIBUTE_READONLY); 309695b41eeSopenharmony_ci } 310695b41eeSopenharmony_ci if (attributes & FILE_ATTRIBUTE_DIRECTORY) { 311695b41eeSopenharmony_ci // remove() deletes both files and directories. On Windows we have to 312695b41eeSopenharmony_ci // select the correct function (DeleteFile will yield Permission Denied when 313695b41eeSopenharmony_ci // used on a directory) 314695b41eeSopenharmony_ci // This fixes the behavior of ninja -t clean in some cases 315695b41eeSopenharmony_ci // https://github.com/ninja-build/ninja/issues/828 316695b41eeSopenharmony_ci if (!RemoveDirectoryA(path.c_str())) { 317695b41eeSopenharmony_ci DWORD win_err = GetLastError(); 318695b41eeSopenharmony_ci if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) { 319695b41eeSopenharmony_ci return 1; 320695b41eeSopenharmony_ci } 321695b41eeSopenharmony_ci // Report remove(), not RemoveDirectory(), for cross-platform consistency. 322695b41eeSopenharmony_ci Error("remove(%s): %s", path.c_str(), GetLastErrorString().c_str()); 323695b41eeSopenharmony_ci return -1; 324695b41eeSopenharmony_ci } 325695b41eeSopenharmony_ci } else { 326695b41eeSopenharmony_ci if (!DeleteFileA(path.c_str())) { 327695b41eeSopenharmony_ci DWORD win_err = GetLastError(); 328695b41eeSopenharmony_ci if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) { 329695b41eeSopenharmony_ci return 1; 330695b41eeSopenharmony_ci } 331695b41eeSopenharmony_ci // Report as remove(), not DeleteFile(), for cross-platform consistency. 332695b41eeSopenharmony_ci Error("remove(%s): %s", path.c_str(), GetLastErrorString().c_str()); 333695b41eeSopenharmony_ci return -1; 334695b41eeSopenharmony_ci } 335695b41eeSopenharmony_ci } 336695b41eeSopenharmony_ci#else 337695b41eeSopenharmony_ci if (remove(path.c_str()) < 0) { 338695b41eeSopenharmony_ci switch (errno) { 339695b41eeSopenharmony_ci case ENOENT: 340695b41eeSopenharmony_ci return 1; 341695b41eeSopenharmony_ci default: 342695b41eeSopenharmony_ci Error("remove(%s): %s", path.c_str(), strerror(errno)); 343695b41eeSopenharmony_ci return -1; 344695b41eeSopenharmony_ci } 345695b41eeSopenharmony_ci } 346695b41eeSopenharmony_ci#endif 347695b41eeSopenharmony_ci return 0; 348695b41eeSopenharmony_ci} 349695b41eeSopenharmony_ci 350695b41eeSopenharmony_civoid RealDiskInterface::AllowStatCache(bool allow) { 351695b41eeSopenharmony_ci#ifdef _WIN32 352695b41eeSopenharmony_ci use_cache_ = allow; 353695b41eeSopenharmony_ci if (!use_cache_) 354695b41eeSopenharmony_ci cache_.clear(); 355695b41eeSopenharmony_ci#endif 356695b41eeSopenharmony_ci} 357695b41eeSopenharmony_ci 358695b41eeSopenharmony_ci#ifdef _WIN32 359695b41eeSopenharmony_cibool RealDiskInterface::AreLongPathsEnabled(void) const { 360695b41eeSopenharmony_ci return long_paths_enabled_; 361695b41eeSopenharmony_ci} 362695b41eeSopenharmony_ci#endif 363