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 <optional>
25 #include <string>
26 #include <vector>
27
28 #include "perfetto/base/build_config.h"
29 #include "perfetto/base/compiler.h"
30 #include "perfetto/base/logging.h"
31 #include "perfetto/base/platform_handle.h"
32 #include "perfetto/base/status.h"
33 #include "perfetto/ext/base/platform.h"
34 #include "perfetto/ext/base/scoped_file.h"
35 #include "perfetto/ext/base/string_utils.h"
36 #include "perfetto/ext/base/utils.h"
37
38 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
39 #include <Windows.h>
40 #include <direct.h>
41 #include <io.h>
42 #include <stringapiset.h>
43 #else
44 #include <dirent.h>
45 #include <unistd.h>
46 #endif
47
48 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
49 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
50 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
51 #define PERFETTO_SET_FILE_PERMISSIONS
52 #include <fcntl.h>
53 #include <grp.h>
54 #include <sys/stat.h>
55 #include <sys/types.h>
56 #include <unistd.h>
57 #endif
58
59 namespace perfetto {
60 namespace base {
61 namespace {
62 constexpr size_t kBufSize = 2048;
63
64 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
65 // Wrap FindClose to: (1) make the return unix-style; (2) deal with stdcall.
CloseFindHandle(HANDLE h)66 int CloseFindHandle(HANDLE h) {
67 return FindClose(h) ? 0 : -1;
68 }
69
ToUtf16(const std::string str)70 std::optional<std::wstring> ToUtf16(const std::string str) {
71 int len = MultiByteToWideChar(CP_UTF8, 0, str.data(),
72 static_cast<int>(str.size()), nullptr, 0);
73 if (len < 0) {
74 return std::nullopt;
75 }
76 std::vector<wchar_t> tmp;
77 tmp.resize(static_cast<std::vector<wchar_t>::size_type>(len));
78 len =
79 MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()),
80 tmp.data(), static_cast<int>(tmp.size()));
81 if (len < 0) {
82 return std::nullopt;
83 }
84 PERFETTO_CHECK(static_cast<std::vector<wchar_t>::size_type>(len) ==
85 tmp.size());
86 return std::wstring(tmp.data(), tmp.size());
87 }
88
89 #endif
90
91 } // namespace
92
Read(int fd,void * dst,size_t dst_size)93 ssize_t Read(int fd, void* dst, size_t dst_size) {
94 ssize_t ret;
95 platform::BeforeMaybeBlockingSyscall();
96 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
97 ret = _read(fd, dst, static_cast<unsigned>(dst_size));
98 #else
99 ret = PERFETTO_EINTR(read(fd, dst, dst_size));
100 #endif
101 platform::AfterMaybeBlockingSyscall();
102 return ret;
103 }
104
ReadFileDescriptor(int fd,std::string * out)105 bool ReadFileDescriptor(int fd, std::string* out) {
106 // Do not override existing data in string.
107 size_t i = out->size();
108
109 struct stat buf {};
110 if (fstat(fd, &buf) != -1) {
111 if (buf.st_size > 0)
112 out->resize(i + static_cast<size_t>(buf.st_size));
113 }
114
115 ssize_t bytes_read;
116 for (;;) {
117 if (out->size() < i + kBufSize)
118 out->resize(out->size() + kBufSize);
119
120 bytes_read = Read(fd, &((*out)[i]), kBufSize);
121 if (bytes_read > 0) {
122 i += static_cast<size_t>(bytes_read);
123 } else {
124 out->resize(i);
125 return bytes_read == 0;
126 }
127 }
128 }
129
ReadPlatformHandle(PlatformHandle h,std::string * out)130 bool ReadPlatformHandle(PlatformHandle h, std::string* out) {
131 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
132 // Do not override existing data in string.
133 size_t i = out->size();
134
135 for (;;) {
136 if (out->size() < i + kBufSize)
137 out->resize(out->size() + kBufSize);
138 DWORD bytes_read = 0;
139 auto res = ::ReadFile(h, &((*out)[i]), kBufSize, &bytes_read, nullptr);
140 if (res && bytes_read > 0) {
141 i += static_cast<size_t>(bytes_read);
142 } else {
143 out->resize(i);
144 const bool is_eof = res && bytes_read == 0;
145 auto err = res ? 0 : GetLastError();
146 // The "Broken pipe" error on Windows is slighly different than Unix:
147 // On Unix: a "broken pipe" error can happen only on the writer side. On
148 // the reader there is no broken pipe, just a EOF.
149 // On windows: the reader also sees a broken pipe error.
150 // Here we normalize on the Unix behavior, treating broken pipe as EOF.
151 return is_eof || err == ERROR_BROKEN_PIPE;
152 }
153 }
154 #else
155 return ReadFileDescriptor(h, out);
156 #endif
157 }
158
ReadFileStream(FILE * f,std::string * out)159 bool ReadFileStream(FILE* f, std::string* out) {
160 return ReadFileDescriptor(fileno(f), out);
161 }
162
ReadFile(const std::string & path,std::string * out)163 bool ReadFile(const std::string& path, std::string* out) {
164 base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
165 if (!fd)
166 return false;
167
168 return ReadFileDescriptor(*fd, out);
169 }
170
WriteAll(int fd,const void * buf,size_t count)171 ssize_t WriteAll(int fd, const void* buf, size_t count) {
172 size_t written = 0;
173 while (written < count) {
174 // write() on windows takes an unsigned int size.
175 uint32_t bytes_left = static_cast<uint32_t>(
176 std::min(count - written, static_cast<size_t>(UINT32_MAX)));
177 platform::BeforeMaybeBlockingSyscall();
178 ssize_t wr = PERFETTO_EINTR(
179 write(fd, static_cast<const char*>(buf) + written, bytes_left));
180 platform::AfterMaybeBlockingSyscall();
181 if (wr == 0)
182 break;
183 if (wr < 0)
184 return wr;
185 written += static_cast<size_t>(wr);
186 }
187 return static_cast<ssize_t>(written);
188 }
189
WriteAllHandle(PlatformHandle h,const void * buf,size_t count)190 ssize_t WriteAllHandle(PlatformHandle h, const void* buf, size_t count) {
191 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
192 DWORD wsize = 0;
193 if (::WriteFile(h, buf, static_cast<DWORD>(count), &wsize, nullptr)) {
194 return wsize;
195 } else {
196 return -1;
197 }
198 #else
199 return WriteAll(h, buf, count);
200 #endif
201 }
202
FlushFile(int fd)203 bool FlushFile(int fd) {
204 PERFETTO_DCHECK(fd != 0);
205 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
206 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
207 return !PERFETTO_EINTR(fdatasync(fd));
208 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
209 return !PERFETTO_EINTR(_commit(fd));
210 #else
211 return !PERFETTO_EINTR(fsync(fd));
212 #endif
213 }
214
Mkdir(const std::string & path)215 bool Mkdir(const std::string& path) {
216 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
217 return _mkdir(path.c_str()) == 0;
218 #else
219 return mkdir(path.c_str(), 0755) == 0;
220 #endif
221 }
222
Rmdir(const std::string & path)223 bool Rmdir(const std::string& path) {
224 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
225 return _rmdir(path.c_str()) == 0;
226 #else
227 return rmdir(path.c_str()) == 0;
228 #endif
229 }
230
CloseFile(int fd)231 int CloseFile(int fd) {
232 return close(fd);
233 }
234
OpenFile(const std::string & path,int flags,FileOpenMode mode)235 ScopedFile OpenFile(const std::string& path, int flags, FileOpenMode mode) {
236 // If a new file might be created, ensure that the permissions for the new
237 // file are explicitly specified.
238 PERFETTO_CHECK((flags & O_CREAT) == 0 || mode != kFileModeInvalid);
239 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
240 // Always use O_BINARY on Windows, to avoid silly EOL translations.
241 ScopedFile fd(_open(path.c_str(), flags | O_BINARY, mode));
242 #else
243 // Always open a ScopedFile with O_CLOEXEC so we can safely fork and exec.
244 ScopedFile fd(open(path.c_str(), flags | O_CLOEXEC, mode));
245 #endif
246 return fd;
247 }
248
OpenFstream(const char * path,const char * mode)249 ScopedFstream OpenFstream(const char* path, const char* mode) {
250 ScopedFstream file;
251 // On Windows fopen interprets filename using the ANSI or OEM codepage but
252 // sqlite3_value_text returns a UTF-8 string. To make sure we interpret the
253 // filename correctly we use _wfopen and a UTF-16 string on windows.
254 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
255 auto w_path = ToUtf16(path);
256 auto w_mode = ToUtf16(mode);
257 if (w_path && w_mode) {
258 file.reset(_wfopen(w_path->c_str(), w_mode->c_str()));
259 }
260 #else
261 file.reset(fopen(path, mode));
262 #endif
263 return file;
264 }
265
FileExists(const std::string & path)266 bool FileExists(const std::string& path) {
267 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
268 return _access(path.c_str(), 0) == 0;
269 #else
270 return access(path.c_str(), F_OK) == 0;
271 #endif
272 }
273
274 // Declared in base/platform_handle.h.
ClosePlatformHandle(PlatformHandle handle)275 int ClosePlatformHandle(PlatformHandle handle) {
276 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
277 // Make the return value UNIX-style.
278 return CloseHandle(handle) ? 0 : -1;
279 #else
280 return close(handle);
281 #endif
282 }
283
ListFilesRecursive(const std::string & dir_path,std::vector<std::string> & output)284 base::Status ListFilesRecursive(const std::string& dir_path,
285 std::vector<std::string>& output) {
286 std::string root_dir_path = dir_path;
287 if (root_dir_path.back() == '\\') {
288 root_dir_path.back() = '/';
289 } else if (root_dir_path.back() != '/') {
290 root_dir_path.push_back('/');
291 }
292
293 // dir_queue contains full paths to the directories. The paths include the
294 // root_dir_path at the beginning and the trailing slash at the end.
295 std::deque<std::string> dir_queue;
296 dir_queue.push_back(root_dir_path);
297
298 while (!dir_queue.empty()) {
299 const std::string cur_dir = std::move(dir_queue.front());
300 dir_queue.pop_front();
301 #if PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
302 return base::ErrStatus("ListFilesRecursive not supported yet");
303 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
304 std::string glob_path = cur_dir + "*";
305 // + 1 because we also have to count the NULL terminator.
306 if (glob_path.length() + 1 > MAX_PATH)
307 return base::ErrStatus("Directory path %s is too long", dir_path.c_str());
308 WIN32_FIND_DATAA ffd;
309
310 base::ScopedResource<HANDLE, CloseFindHandle, nullptr, false,
311 base::PlatformHandleChecker>
312 hFind(FindFirstFileA(glob_path.c_str(), &ffd));
313 if (!hFind) {
314 // For empty directories, there should be at least one entry '.'.
315 // If FindFirstFileA returns INVALID_HANDLE_VALUE, this means directory
316 // couldn't be accessed.
317 return base::ErrStatus("Failed to open directory %s", cur_dir.c_str());
318 }
319 do {
320 if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
321 continue;
322 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
323 std::string subdir_path = cur_dir + ffd.cFileName + '/';
324 dir_queue.push_back(subdir_path);
325 } else {
326 const std::string full_path = cur_dir + ffd.cFileName;
327 PERFETTO_CHECK(full_path.length() > root_dir_path.length());
328 output.push_back(full_path.substr(root_dir_path.length()));
329 }
330 } while (FindNextFileA(*hFind, &ffd));
331 #else
332 ScopedDir dir = ScopedDir(opendir(cur_dir.c_str()));
333 if (!dir) {
334 return base::ErrStatus("Failed to open directory %s", cur_dir.c_str());
335 }
336 for (auto* dirent = readdir(dir.get()); dirent != nullptr;
337 dirent = readdir(dir.get())) {
338 if (strcmp(dirent->d_name, ".") == 0 ||
339 strcmp(dirent->d_name, "..") == 0) {
340 continue;
341 }
342 if (dirent->d_type == DT_DIR) {
343 dir_queue.push_back(cur_dir + dirent->d_name + '/');
344 } else if (dirent->d_type == DT_REG) {
345 const std::string full_path = cur_dir + dirent->d_name;
346 PERFETTO_CHECK(full_path.length() > root_dir_path.length());
347 output.push_back(full_path.substr(root_dir_path.length()));
348 }
349 }
350 #endif
351 }
352 return base::OkStatus();
353 }
354
GetFileExtension(const std::string & filename)355 std::string GetFileExtension(const std::string& filename) {
356 auto ext_idx = filename.rfind('.');
357 if (ext_idx == std::string::npos)
358 return std::string();
359 return filename.substr(ext_idx);
360 }
361
SetFilePermissions(const std::string & file_path,const std::string & group_name_or_id,const std::string & mode_bits)362 base::Status SetFilePermissions(const std::string& file_path,
363 const std::string& group_name_or_id,
364 const std::string& mode_bits) {
365 #ifdef PERFETTO_SET_FILE_PERMISSIONS
366 PERFETTO_CHECK(!file_path.empty());
367 PERFETTO_CHECK(!group_name_or_id.empty());
368
369 // Default |group_id| to -1 for not changing the group ownership.
370 gid_t group_id = static_cast<gid_t>(-1);
371 auto maybe_group_id = base::StringToUInt32(group_name_or_id);
372 if (maybe_group_id) { // A numerical group ID.
373 group_id = *maybe_group_id;
374 } else { // A group name.
375 struct group* file_group = nullptr;
376 // Query the group ID of |group|.
377 do {
378 file_group = getgrnam(group_name_or_id.c_str());
379 } while (file_group == nullptr && errno == EINTR);
380 if (file_group == nullptr) {
381 return base::ErrStatus("Failed to get group information of %s ",
382 group_name_or_id.c_str());
383 }
384 group_id = file_group->gr_gid;
385 }
386
387 if (PERFETTO_EINTR(chown(file_path.c_str(), geteuid(), group_id))) {
388 return base::ErrStatus("Failed to chown %s ", file_path.c_str());
389 }
390
391 // |mode| accepts values like "0660" as "rw-rw----" mode bits.
392 auto mode_value = base::StringToInt32(mode_bits, 8);
393 if (!(mode_bits.size() == 4 && mode_value.has_value())) {
394 return base::ErrStatus(
395 "The chmod mode bits must be a 4-digit octal number, e.g. 0660");
396 }
397 if (PERFETTO_EINTR(
398 chmod(file_path.c_str(), static_cast<mode_t>(mode_value.value())))) {
399 return base::ErrStatus("Failed to chmod %s", file_path.c_str());
400 }
401 return base::OkStatus();
402 #else
403 base::ignore_result(file_path);
404 base::ignore_result(group_name_or_id);
405 base::ignore_result(mode_bits);
406 return base::ErrStatus(
407 "Setting file permissions is not supported on this platform");
408 #endif
409 }
410
GetFileSize(const std::string & file_path)411 std::optional<uint64_t> GetFileSize(const std::string& file_path) {
412 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
413 // This does not use base::OpenFile to avoid getting an exclusive lock.
414 base::ScopedPlatformHandle fd(
415 CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
416 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
417 #else
418 base::ScopedFile fd(base::OpenFile(file_path, O_RDONLY | O_CLOEXEC));
419 #endif
420 if (!fd) {
421 return std::nullopt;
422 }
423 return GetFileSize(*fd);
424 }
425
GetFileSize(PlatformHandle fd)426 std::optional<uint64_t> GetFileSize(PlatformHandle fd) {
427 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
428 LARGE_INTEGER file_size;
429 file_size.QuadPart = 0;
430 if (!GetFileSizeEx(fd, &file_size)) {
431 return std::nullopt;
432 }
433 static_assert(sizeof(decltype(file_size.QuadPart)) <= sizeof(uint64_t));
434 return static_cast<uint64_t>(file_size.QuadPart);
435 #else
436 struct stat buf {};
437 if (fstat(fd, &buf) == -1) {
438 return std::nullopt;
439 }
440 static_assert(sizeof(decltype(buf.st_size)) <= sizeof(uint64_t));
441 return static_cast<uint64_t>(buf.st_size);
442 #endif
443 }
444
445 } // namespace base
446 } // namespace perfetto
447