xref: /aosp_15_r20/external/perfetto/src/base/file_utils.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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