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