23 #include <sys/types.h> 40 string DirName(
const string& path) {
42 static const char kPathSeparators[] =
"\\/";
44 static const char kPathSeparators[] =
"/";
46 static const char*
const kEnd = kPathSeparators +
sizeof(kPathSeparators) - 1;
48 string::size_type slash_pos = path.find_last_of(kPathSeparators);
49 if (slash_pos == string::npos)
51 while (slash_pos > 0 &&
52 std::find(kPathSeparators, kEnd, path[slash_pos - 1]) != kEnd)
54 return path.substr(0, slash_pos);
57 int MakeDir(
const string& path) {
59 return _mkdir(path.c_str());
61 return mkdir(path.c_str(), 0777);
66 TimeStamp TimeStampFromFileTime(
const FILETIME& filetime) {
73 return (
TimeStamp)mtime - 12622770400LL * (1000000000LL / 100);
76 TimeStamp StatSingleFile(
const string& path,
string* err) {
77 WIN32_FILE_ATTRIBUTE_DATA attrs;
78 if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) {
79 DWORD win_err = GetLastError();
80 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)
82 *err =
"GetFileAttributesEx(" + path +
"): " + GetLastErrorString();
85 return TimeStampFromFileTime(attrs.ftLastWriteTime);
88 bool IsWindows7OrLater() {
89 OSVERSIONINFOEX version_info =
90 {
sizeof(OSVERSIONINFOEX), 6, 1, 0, 0, {0}, 0, 0, 0, 0, 0};
91 DWORDLONG comparison = 0;
92 VER_SET_CONDITION(comparison, VER_MAJORVERSION, VER_GREATER_EQUAL);
93 VER_SET_CONDITION(comparison, VER_MINORVERSION, VER_GREATER_EQUAL);
94 return VerifyVersionInfo(
95 &version_info, VER_MAJORVERSION | VER_MINORVERSION, comparison);
98 bool StatAllFilesInDir(
const string& dir, map<string, TimeStamp>* stamps,
101 static bool can_use_basic_info = IsWindows7OrLater();
103 const FINDEX_INFO_LEVELS kFindExInfoBasic =
104 static_cast<FINDEX_INFO_LEVELS
>(1);
105 FINDEX_INFO_LEVELS level =
106 can_use_basic_info ? kFindExInfoBasic : FindExInfoStandard;
107 WIN32_FIND_DATAA ffd;
108 HANDLE find_handle = FindFirstFileExA((dir +
"\\*").c_str(), level, &ffd,
109 FindExSearchNameMatch, NULL, 0);
111 if (find_handle == INVALID_HANDLE_VALUE) {
112 DWORD win_err = GetLastError();
113 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)
115 *err =
"FindFirstFileExA(" + dir +
"): " + GetLastErrorString();
119 string lowername = ffd.cFileName;
120 if (lowername ==
"..") {
125 transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
126 stamps->insert(make_pair(lowername,
127 TimeStampFromFileTime(ffd.ftLastWriteTime)));
128 }
while (FindNextFileA(find_handle, &ffd));
129 FindClose(find_handle);
139 string dir = DirName(path);
145 Error(
"%s", err.c_str());
152 bool success = MakeDirs(dir);
165 if (!path.empty() && path[0] !=
'\\' && path.size() > MAX_PATH) {
166 ostringstream err_stream;
167 err_stream <<
"Stat(" << path <<
"): Filename longer than " << MAX_PATH
169 *err = err_stream.str();
173 return StatSingleFile(path, err);
175 string dir = DirName(path);
176 string base(path.substr(dir.size() ? dir.size() + 1 : 0));
183 transform(dir.begin(), dir.end(), dir.begin(), ::tolower);
184 transform(base.begin(), base.end(), base.begin(), ::tolower);
186 Cache::iterator ci = cache_.find(dir);
187 if (ci == cache_.end()) {
188 ci = cache_.insert(make_pair(dir, DirCache())).first;
189 if (!StatAllFilesInDir(dir.empty() ?
"." : dir, &ci->second, err)) {
194 DirCache::iterator di = ci->second.find(base);
195 return di != ci->second.end() ? di->second : 0;
198 if (stat(path.c_str(), &st) < 0) {
199 if (errno == ENOENT || errno == ENOTDIR)
201 *err =
"stat(" + path +
"): " + strerror(errno);
207 if (st.st_mtime == 0)
210 return (
int64_t)st.st_mtime * 1000000000LL + st.st_mtime_n;
211 #elif defined(__APPLE__) 212 return ((
int64_t)st.st_mtimespec.tv_sec * 1000000000LL +
213 st.st_mtimespec.tv_nsec);
214 #elif defined(st_mtime) // A macro, so we're likely on modern POSIX. 215 return (
int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec;
217 return (
int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec;
223 FILE* fp = fopen(path.c_str(),
"w");
225 Error(
"WriteFile(%s): Unable to create file. %s",
226 path.c_str(), strerror(errno));
230 if (fwrite(contents.data(), 1, contents.length(), fp) < contents.length()) {
231 Error(
"WriteFile(%s): Unable to write to the file. %s",
232 path.c_str(), strerror(errno));
237 if (fclose(fp) == EOF) {
238 Error(
"WriteFile(%s): Unable to close the file. %s",
239 path.c_str(), strerror(errno));
247 if (::MakeDir(path) < 0) {
248 if (errno == EEXIST) {
251 Error(
"mkdir(%s): %s", path.c_str(), strerror(errno));
260 switch (::
ReadFile(path, contents, err)) {
262 case -ENOENT:
return NotFound;
263 default:
return OtherError;
268 if (
remove(path.c_str()) < 0) {
273 Error(
"remove(%s): %s", path.c_str(), strerror(errno));
virtual bool MakeDir(const std::string &path)
Create a directory, returning false on failure.
virtual TimeStamp Stat(const std::string &path, std::string *err) const
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
signed long long int64_t
A 64-bit integer type.
virtual bool WriteFile(const std::string &path, const std::string &contents)
Create a file, with the specified name and contents Returns true on success, false on failure...
Status
Result of ReadFile.
int ReadFile(const string &path, string *contents, string *err)
#define METRIC_RECORD(name)
The primary interface to metrics.
virtual Status ReadFile(const std::string &path, std::string *contents, std::string *err)
Read and store in given string.
bool MakeDirs(const std::string &path)
Create all the parent directories for path; like mkdir -p basename path.
void AllowStatCache(bool allow)
Whether stat information can be cached. Only has an effect on Windows.
unsigned long long uint64_t
virtual int RemoveFile(const std::string &path)
Remove the file named path.
void Error(const char *msg,...)
Log an error message.