• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/ext/base/file_utils.h"
18 
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 
22 #include <algorithm>
23 #include <deque>
24 #include <string>
25 #include <vector>
26 
27 #include "perfetto/base/build_config.h"
28 #include "perfetto/base/logging.h"
29 #include "perfetto/base/platform_handle.h"
30 #include "perfetto/base/status.h"
31 #include "perfetto/ext/base/scoped_file.h"
32 #include "perfetto/ext/base/utils.h"
33 
34 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
35 #include <Windows.h>
36 #include <direct.h>
37 #include <io.h>
38 #else
39 #include <dirent.h>
40 #include <unistd.h>
41 #endif
42 
43 namespace perfetto {
44 namespace base {
45 namespace {
46 constexpr size_t kBufSize = 2048;
47 
48 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
49 // Wrap FindClose to: (1) make the return unix-style; (2) deal with stdcall.
CloseFindHandle(HANDLE h)50 int CloseFindHandle(HANDLE h) {
51   return FindClose(h) ? 0 : -1;
52 }
53 #endif
54 
55 }  // namespace
56 
Read(int fd,void * dst,size_t dst_size)57 ssize_t Read(int fd, void* dst, size_t dst_size) {
58 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
59   return _read(fd, dst, static_cast<unsigned>(dst_size));
60 #else
61   return PERFETTO_EINTR(read(fd, dst, dst_size));
62 #endif
63 }
64 
ReadFileDescriptor(int fd,std::string * out)65 bool ReadFileDescriptor(int fd, std::string* out) {
66   // Do not override existing data in string.
67   size_t i = out->size();
68 
69   struct stat buf {};
70   if (fstat(fd, &buf) != -1) {
71     if (buf.st_size > 0)
72       out->resize(i + static_cast<size_t>(buf.st_size));
73   }
74 
75   ssize_t bytes_read;
76   for (;;) {
77     if (out->size() < i + kBufSize)
78       out->resize(out->size() + kBufSize);
79 
80     bytes_read = Read(fd, &((*out)[i]), kBufSize);
81     if (bytes_read > 0) {
82       i += static_cast<size_t>(bytes_read);
83     } else {
84       out->resize(i);
85       return bytes_read == 0;
86     }
87   }
88 }
89 
ReadPlatformHandle(PlatformHandle h,std::string * out)90 bool ReadPlatformHandle(PlatformHandle h, std::string* out) {
91 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
92   // Do not override existing data in string.
93   size_t i = out->size();
94 
95   for (;;) {
96     if (out->size() < i + kBufSize)
97       out->resize(out->size() + kBufSize);
98     DWORD bytes_read = 0;
99     auto res = ::ReadFile(h, &((*out)[i]), kBufSize, &bytes_read, nullptr);
100     if (res && bytes_read > 0) {
101       i += static_cast<size_t>(bytes_read);
102     } else {
103       out->resize(i);
104       const bool is_eof = res && bytes_read == 0;
105       auto err = res ? 0 : GetLastError();
106       // The "Broken pipe" error on Windows is slighly different than Unix:
107       // On Unix: a "broken pipe" error can happen only on the writer side. On
108       // the reader there is no broken pipe, just a EOF.
109       // On windows: the reader also sees a broken pipe error.
110       // Here we normalize on the Unix behavior, treating broken pipe as EOF.
111       return is_eof || err == ERROR_BROKEN_PIPE;
112     }
113   }
114 #else
115   return ReadFileDescriptor(h, out);
116 #endif
117 }
118 
ReadFileStream(FILE * f,std::string * out)119 bool ReadFileStream(FILE* f, std::string* out) {
120   return ReadFileDescriptor(fileno(f), out);
121 }
122 
ReadFile(const std::string & path,std::string * out)123 bool ReadFile(const std::string& path, std::string* out) {
124   base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
125   if (!fd)
126     return false;
127 
128   return ReadFileDescriptor(*fd, out);
129 }
130 
WriteAll(int fd,const void * buf,size_t count)131 ssize_t WriteAll(int fd, const void* buf, size_t count) {
132   size_t written = 0;
133   while (written < count) {
134     // write() on windows takes an unsigned int size.
135     uint32_t bytes_left = static_cast<uint32_t>(
136         std::min(count - written, static_cast<size_t>(UINT32_MAX)));
137     ssize_t wr = PERFETTO_EINTR(
138         write(fd, static_cast<const char*>(buf) + written, bytes_left));
139     if (wr == 0)
140       break;
141     if (wr < 0)
142       return wr;
143     written += static_cast<size_t>(wr);
144   }
145   return static_cast<ssize_t>(written);
146 }
147 
WriteAllHandle(PlatformHandle h,const void * buf,size_t count)148 ssize_t WriteAllHandle(PlatformHandle h, const void* buf, size_t count) {
149 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
150   DWORD wsize = 0;
151   if (::WriteFile(h, buf, static_cast<DWORD>(count), &wsize, nullptr)) {
152     return wsize;
153   } else {
154     return -1;
155   }
156 #else
157   return WriteAll(h, buf, count);
158 #endif
159 }
160 
FlushFile(int fd)161 bool FlushFile(int fd) {
162   PERFETTO_DCHECK(fd != 0);
163 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
164     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
165   return !PERFETTO_EINTR(fdatasync(fd));
166 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
167   return !PERFETTO_EINTR(_commit(fd));
168 #else
169   return !PERFETTO_EINTR(fsync(fd));
170 #endif
171 }
172 
Mkdir(const std::string & path)173 bool Mkdir(const std::string& path) {
174 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
175   return _mkdir(path.c_str()) == 0;
176 #else
177   return mkdir(path.c_str(), 0755) == 0;
178 #endif
179 }
180 
Rmdir(const std::string & path)181 bool Rmdir(const std::string& path) {
182 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
183   return _rmdir(path.c_str()) == 0;
184 #else
185   return rmdir(path.c_str()) == 0;
186 #endif
187 }
188 
CloseFile(int fd)189 int CloseFile(int fd) {
190   return close(fd);
191 }
192 
OpenFile(const std::string & path,int flags,FileOpenMode mode)193 ScopedFile OpenFile(const std::string& path, int flags, FileOpenMode mode) {
194   PERFETTO_DCHECK((flags & O_CREAT) == 0 || mode != kFileModeInvalid);
195 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
196   // Always use O_BINARY on Windows, to avoid silly EOL translations.
197   ScopedFile fd(_open(path.c_str(), flags | O_BINARY, mode));
198 #else
199   // Always open a ScopedFile with O_CLOEXEC so we can safely fork and exec.
200   ScopedFile fd(open(path.c_str(), flags | O_CLOEXEC, mode));
201 #endif
202   return fd;
203 }
204 
FileExists(const std::string & path)205 bool FileExists(const std::string& path) {
206 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
207   return _access(path.c_str(), 0) == 0;
208 #else
209   return access(path.c_str(), F_OK) == 0;
210 #endif
211 }
212 
213 // Declared in base/platform_handle.h.
ClosePlatformHandle(PlatformHandle handle)214 int ClosePlatformHandle(PlatformHandle handle) {
215 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
216   // Make the return value UNIX-style.
217   return CloseHandle(handle) ? 0 : -1;
218 #else
219   return close(handle);
220 #endif
221 }
222 
ListFilesRecursive(const std::string & dir_path,std::vector<std::string> & output)223 base::Status ListFilesRecursive(const std::string& dir_path,
224                                 std::vector<std::string>& output) {
225   std::string root_dir_path = dir_path;
226   if (root_dir_path.back() == '\\') {
227     root_dir_path.back() = '/';
228   } else if (root_dir_path.back() != '/') {
229     root_dir_path.push_back('/');
230   }
231 
232   // dir_queue contains full paths to the directories. The paths include the
233   // root_dir_path at the beginning and the trailing slash at the end.
234   std::deque<std::string> dir_queue;
235   dir_queue.push_back(root_dir_path);
236 
237   while (!dir_queue.empty()) {
238     const std::string cur_dir = std::move(dir_queue.front());
239     dir_queue.pop_front();
240 #if PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
241     return base::ErrStatus("ListFilesRecursive not supported yet");
242 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
243     std::string glob_path = cur_dir + "*";
244     // + 1 because we also have to count the NULL terminator.
245     if (glob_path.length() + 1 > MAX_PATH)
246       return base::ErrStatus("Directory path %s is too long", dir_path.c_str());
247     WIN32_FIND_DATAA ffd;
248 
249     base::ScopedResource<HANDLE, CloseFindHandle, nullptr, false,
250                          base::PlatformHandleChecker>
251         hFind(FindFirstFileA(glob_path.c_str(), &ffd));
252     if (!hFind) {
253       // For empty directories, there should be at least one entry '.'.
254       // If FindFirstFileA returns INVALID_HANDLE_VALUE, this means directory
255       // couldn't be accessed.
256       return base::ErrStatus("Failed to open directory %s", cur_dir.c_str());
257     }
258     do {
259       if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
260         continue;
261       if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
262         std::string subdir_path = cur_dir + ffd.cFileName + '/';
263         dir_queue.push_back(subdir_path);
264       } else {
265         const std::string full_path = cur_dir + ffd.cFileName;
266         PERFETTO_CHECK(full_path.length() > root_dir_path.length());
267         output.push_back(full_path.substr(root_dir_path.length()));
268       }
269     } while (FindNextFileA(*hFind, &ffd));
270 #else
271     ScopedDir dir = ScopedDir(opendir(cur_dir.c_str()));
272     if (!dir) {
273       return base::ErrStatus("Failed to open directory %s", cur_dir.c_str());
274     }
275     for (auto* dirent = readdir(dir.get()); dirent != nullptr;
276          dirent = readdir(dir.get())) {
277       if (strcmp(dirent->d_name, ".") == 0 ||
278           strcmp(dirent->d_name, "..") == 0) {
279         continue;
280       }
281       if (dirent->d_type == DT_DIR) {
282         dir_queue.push_back(cur_dir + dirent->d_name + '/');
283       } else if (dirent->d_type == DT_REG) {
284         const std::string full_path = cur_dir + dirent->d_name;
285         PERFETTO_CHECK(full_path.length() > root_dir_path.length());
286         output.push_back(full_path.substr(root_dir_path.length()));
287       }
288     }
289 #endif
290   }
291   return base::OkStatus();
292 }
293 
GetFileExtension(const std::string & filename)294 std::string GetFileExtension(const std::string& filename) {
295   auto ext_idx = filename.rfind('.');
296   if (ext_idx == std::string::npos)
297     return std::string();
298   return filename.substr(ext_idx);
299 }
300 
GetFileSize(const std::string & file_path)301 base::Optional<size_t> GetFileSize(const std::string& file_path) {
302 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
303   HANDLE file =
304       CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
305                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
306   if (file == INVALID_HANDLE_VALUE) {
307     return nullopt;
308   }
309   LARGE_INTEGER file_size;
310   file_size.QuadPart = 0;
311   BOOL ok = GetFileSizeEx(file, &file_size);
312   CloseHandle(file);
313   if (!ok) {
314     return nullopt;
315   }
316   return static_cast<size_t>(file_size.QuadPart);
317 #else
318   base::ScopedFile fd(base::OpenFile(file_path, O_RDONLY | O_CLOEXEC));
319   if (!fd) {
320     return nullopt;
321   }
322   struct stat buf{};
323   if (fstat(*fd, &buf) == -1) {
324     return nullopt;
325   }
326   return static_cast<size_t>(buf.st_size);
327 #endif
328 }
329 
330 }  // namespace base
331 }  // namespace perfetto
332