23 #include <sys/types.h>
41 string DirName(
const string& path) {
43 static const char kPathSeparators[] =
"\\/";
45 static const char kPathSeparators[] =
"/";
47 static const char*
const kEnd = kPathSeparators +
sizeof(kPathSeparators) - 1;
49 string::size_type slash_pos = path.find_last_of(kPathSeparators);
50 if (slash_pos == string::npos)
52 while (slash_pos > 0 &&
53 std::find(kPathSeparators, kEnd, path[slash_pos - 1]) != kEnd)
55 return path.substr(0, slash_pos);
58 int MakeDir(
const string& path) {
60 return _mkdir(path.c_str());
62 return mkdir(path.c_str(), 0777);
67 TimeStamp TimeStampFromFileTime(
const FILETIME& filetime) {
74 return (
TimeStamp)mtime - 12622770400LL * (1000000000LL / 100);
77 TimeStamp StatSingleFile(
const string& path,
string* err) {
78 WIN32_FILE_ATTRIBUTE_DATA attrs;
79 if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) {
80 DWORD win_err = GetLastError();
81 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)
83 *err =
"GetFileAttributesEx(" + path +
"): " + GetLastErrorString();
86 return TimeStampFromFileTime(attrs.ftLastWriteTime);
89 bool IsWindows7OrLater() {
90 OSVERSIONINFOEX version_info =
91 {
sizeof(OSVERSIONINFOEX), 6, 1, 0, 0, {0}, 0, 0, 0, 0, 0};
92 DWORDLONG comparison = 0;
93 VER_SET_CONDITION(comparison, VER_MAJORVERSION, VER_GREATER_EQUAL);
94 VER_SET_CONDITION(comparison, VER_MINORVERSION, VER_GREATER_EQUAL);
95 return VerifyVersionInfo(
96 &version_info, VER_MAJORVERSION | VER_MINORVERSION, comparison);
99 bool StatAllFilesInDir(
const string& dir, map<string, TimeStamp>* stamps,
102 static bool can_use_basic_info = IsWindows7OrLater();
104 const FINDEX_INFO_LEVELS kFindExInfoBasic =
105 static_cast<FINDEX_INFO_LEVELS
>(1);
106 FINDEX_INFO_LEVELS level =
107 can_use_basic_info ? kFindExInfoBasic : FindExInfoStandard;
108 WIN32_FIND_DATAA ffd;
109 HANDLE find_handle = FindFirstFileExA((dir +
"\\*").c_str(), level, &ffd,
110 FindExSearchNameMatch, NULL, 0);
112 if (find_handle == INVALID_HANDLE_VALUE) {
113 DWORD win_err = GetLastError();
114 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND ||
115 win_err == ERROR_DIRECTORY)
117 *err =
"FindFirstFileExA(" + dir +
"): " + GetLastErrorString();
121 string lowername = ffd.cFileName;
122 if (lowername ==
"..") {
127 transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
128 stamps->insert(make_pair(lowername,
129 TimeStampFromFileTime(ffd.ftLastWriteTime)));
130 }
while (FindNextFileA(find_handle, &ffd));
131 FindClose(find_handle);
141 string dir = DirName(path);
147 Error(
"%s", err.c_str());
154 bool success = MakeDirs(dir);
163 : use_cache_(
false), long_paths_enabled_(
false) {
165 HINSTANCE ntdll_lib = ::GetModuleHandleW(L
"ntdll");
167 typedef BOOLEAN(WINAPI FunctionType)();
168 auto* func_ptr = FunctionCast<FunctionType*>(
169 ::GetProcAddress(ntdll_lib,
"RtlAreLongPathsEnabled"));
171 long_paths_enabled_ = (*func_ptr)();
184 if (!path.empty() && !AreLongPathsEnabled() && path[0] !=
'\\' &&
185 path.size() > MAX_PATH) {
186 ostringstream err_stream;
187 err_stream <<
"Stat(" << path <<
"): Filename longer than " << MAX_PATH
189 *err = err_stream.str();
193 return StatSingleFile(path, err);
195 string dir = DirName(path);
196 string base(path.substr(dir.size() ? dir.size() + 1 : 0));
203 string dir_lowercase = dir;
204 transform(dir.begin(), dir.end(), dir_lowercase.begin(), ::tolower);
205 transform(base.begin(), base.end(), base.begin(), ::tolower);
207 Cache::iterator ci = cache_.find(dir_lowercase);
208 if (ci == cache_.end()) {
209 ci = cache_.insert(make_pair(dir_lowercase, DirCache())).first;
210 if (!StatAllFilesInDir(dir.empty() ?
"." : dir, &ci->second, err)) {
215 DirCache::iterator di = ci->second.find(base);
216 return di != ci->second.end() ? di->second : 0;
218 #ifdef __USE_LARGEFILE64
220 if (stat64(path.c_str(), &st) < 0) {
223 if (stat(path.c_str(), &st) < 0) {
225 if (errno == ENOENT || errno == ENOTDIR)
227 *err =
"stat(" + path +
"): " + strerror(errno);
233 if (st.st_mtime == 0)
236 return (
int64_t)st.st_mtime * 1000000000LL + st.st_mtime_n;
237 #elif defined(__APPLE__)
238 return ((
int64_t)st.st_mtimespec.tv_sec * 1000000000LL +
239 st.st_mtimespec.tv_nsec);
240 #elif defined(st_mtime)
241 return (
int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec;
243 return (
int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec;
249 bool crlf_on_windows) {
250 FILE* fp = fopen(path.c_str(),
252 crlf_on_windows ?
"w" :
"wb");
255 (void)crlf_on_windows;
258 Error(
"WriteFile(%s): Unable to create file. %s",
259 path.c_str(), strerror(errno));
263 if (fwrite(contents.data(), 1, contents.length(), fp) < contents.length()) {
264 Error(
"WriteFile(%s): Unable to write to the file. %s",
265 path.c_str(), strerror(errno));
270 if (fclose(fp) == EOF) {
271 Error(
"WriteFile(%s): Unable to close the file. %s",
272 path.c_str(), strerror(errno));
280 if (::MakeDir(path) < 0) {
281 if (errno == EEXIST) {
284 Error(
"mkdir(%s): %s", path.c_str(), strerror(errno));
293 switch (::
ReadFile(path, contents, err)) {
295 case -ENOENT:
return NotFound;
296 default:
return OtherError;
302 DWORD attributes = GetFileAttributesA(path.c_str());
303 if (attributes == INVALID_FILE_ATTRIBUTES) {
304 DWORD win_err = GetLastError();
305 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) {
308 }
else if (attributes & FILE_ATTRIBUTE_READONLY) {
313 SetFileAttributesA(path.c_str(), attributes & ~FILE_ATTRIBUTE_READONLY);
315 if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
321 if (!RemoveDirectoryA(path.c_str())) {
322 DWORD win_err = GetLastError();
323 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) {
327 Error(
"remove(%s): %s", path.c_str(), GetLastErrorString().c_str());
331 if (!DeleteFileA(path.c_str())) {
332 DWORD win_err = GetLastError();
333 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) {
337 Error(
"remove(%s): %s", path.c_str(), GetLastErrorString().c_str());
342 if (remove(path.c_str()) < 0) {
347 Error(
"remove(%s): %s", path.c_str(), strerror(errno));
364 bool RealDiskInterface::AreLongPathsEnabled(
void)
const {
365 return long_paths_enabled_;
#define METRIC_RECORD(name)
The primary interface to metrics.
bool MakeDirs(const std::string &path)
Create all the parent directories for path; like mkdir -p basename path.
Status
Result of ReadFile.
Status ReadFile(const std::string &path, std::string *contents, std::string *err) override
Read and store in given string.
void AllowStatCache(bool allow)
Whether stat information can be cached. Only has an effect on Windows.
bool WriteFile(const std::string &path, const std::string &contents, bool crlf_on_windows) override
Create a file, with the specified name and contents If crlf_on_windows is true, will be converted t...
int RemoveFile(const std::string &path) override
Remove the file named path.
TimeStamp Stat(const std::string &path, std::string *err) const override
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
bool MakeDir(const std::string &path) override
Create a directory, returning false on failure.
void Error(const char *msg, va_list ap)
int ReadFile(const string &path, string *contents, string *err)
unsigned long long uint64_t
signed long long int64_t
A 64-bit integer type.