1 /*
2 * Copyright (C) 2017 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 "common/libs/utils/files.h"
18 #include "common/libs/utils/contains.h"
19 #include "common/libs/utils/inotify.h"
20
21 #include <dirent.h>
22 #include <fcntl.h>
23 #include <ftw.h>
24 #include <libgen.h>
25 #include <linux/fiemap.h>
26 #include <linux/fs.h>
27 #include <sched.h>
28 #include <sys/inotify.h>
29 #include <sys/ioctl.h>
30 #include <sys/select.h>
31 #include <sys/sendfile.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35
36 #include <array>
37 #include <cerrno>
38 #include <chrono>
39 #include <climits>
40 #include <cstdio>
41 #include <cstdlib>
42 #include <cstring>
43 #include <fstream>
44 #include <ios>
45 #include <iosfwd>
46 #include <istream>
47 #include <memory>
48 #include <ostream>
49 #include <ratio>
50 #include <string>
51 #include <vector>
52
53 #include <android-base/logging.h>
54 #include <android-base/macros.h>
55
56 #include "android-base/strings.h"
57 #include "common/libs/fs/shared_fd.h"
58 #include "common/libs/utils/result.h"
59 #include "common/libs/utils/scope_guard.h"
60 #include "common/libs/utils/subprocess.h"
61 #include "common/libs/utils/users.h"
62
63 namespace cuttlefish {
64
FileExists(const std::string & path,bool follow_symlinks)65 bool FileExists(const std::string& path, bool follow_symlinks) {
66 struct stat st {};
67 return (follow_symlinks ? stat : lstat)(path.c_str(), &st) == 0;
68 }
69
FileHasContent(const std::string & path)70 bool FileHasContent(const std::string& path) {
71 return FileSize(path) > 0;
72 }
73
DirectoryContents(const std::string & path)74 Result<std::vector<std::string>> DirectoryContents(const std::string& path) {
75 std::vector<std::string> ret;
76 std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir);
77 CF_EXPECT(dir != nullptr, "Could not read from dir \"" << path << "\"");
78 struct dirent* ent{};
79 while ((ent = readdir(dir.get()))) {
80 ret.emplace_back(ent->d_name);
81 }
82 return ret;
83 }
84
DirectoryExists(const std::string & path,bool follow_symlinks)85 bool DirectoryExists(const std::string& path, bool follow_symlinks) {
86 struct stat st {};
87 if ((follow_symlinks ? stat : lstat)(path.c_str(), &st) == -1) {
88 return false;
89 }
90 if ((st.st_mode & S_IFMT) != S_IFDIR) {
91 return false;
92 }
93 return true;
94 }
95
EnsureDirectoryExists(const std::string & directory_path,const mode_t mode,const std::string & group_name)96 Result<void> EnsureDirectoryExists(const std::string& directory_path,
97 const mode_t mode,
98 const std::string& group_name) {
99 if (DirectoryExists(directory_path)) {
100 return {};
101 }
102 const auto parent_dir = cpp_dirname(directory_path);
103 if (parent_dir.size() > 1) {
104 EnsureDirectoryExists(parent_dir, mode, group_name);
105 }
106 LOG(DEBUG) << "Setting up " << directory_path;
107 if (mkdir(directory_path.c_str(), mode) < 0 && errno != EEXIST) {
108 return CF_ERRNO("Failed to create directory: \"" << directory_path << "\"");
109 }
110
111 if (group_name != "") {
112 ChangeGroup(directory_path, group_name);
113 }
114
115 return {};
116 }
117
ChangeGroup(const std::string & path,const std::string & group_name)118 Result<void> ChangeGroup(const std::string& path,
119 const std::string& group_name) {
120 auto groupId = GroupIdFromName(group_name);
121
122 if (groupId == -1) {
123 return CF_ERR("Failed to get group id: ") << group_name;
124 }
125
126 if (chown(path.c_str(), -1, groupId) != 0) {
127 return CF_ERRNO("Feailed to set group for path: "
128 << path << ", " << group_name << ", " << strerror(errno));
129 }
130
131 return {};
132 }
133
CanAccess(const std::string & path,const int mode)134 bool CanAccess(const std::string& path, const int mode) {
135 return access(path.c_str(), mode) == 0;
136 }
137
IsDirectoryEmpty(const std::string & path)138 bool IsDirectoryEmpty(const std::string& path) {
139 auto direc = ::opendir(path.c_str());
140 if (!direc) {
141 LOG(ERROR) << "IsDirectoryEmpty test failed with " << path
142 << " as it failed to be open" << std::endl;
143 return false;
144 }
145
146 decltype(::readdir(direc)) sub = nullptr;
147 int cnt {0};
148 while ( (sub = ::readdir(direc)) ) {
149 cnt++;
150 if (cnt > 2) {
151 LOG(ERROR) << "IsDirectoryEmpty test failed with " << path
152 << " as it exists but not empty" << std::endl;
153 return false;
154 }
155 }
156 return true;
157 }
158
RecursivelyRemoveDirectory(const std::string & path)159 bool RecursivelyRemoveDirectory(const std::string& path) {
160 // Copied from libbase TemporaryDir destructor.
161 auto callback = [](const char* child, const struct stat*, int file_type,
162 struct FTW*) -> int {
163 switch (file_type) {
164 case FTW_D:
165 case FTW_DP:
166 case FTW_DNR:
167 if (rmdir(child) == -1) {
168 PLOG(ERROR) << "rmdir " << child;
169 }
170 break;
171 case FTW_NS:
172 default:
173 if (rmdir(child) != -1) {
174 break;
175 }
176 // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
177 FALLTHROUGH_INTENDED;
178 case FTW_F:
179 case FTW_SL:
180 case FTW_SLN:
181 if (unlink(child) == -1) {
182 PLOG(ERROR) << "unlink " << child;
183 }
184 break;
185 }
186 return 0;
187 };
188
189 return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) ==
190 0;
191 }
192
193 namespace {
194
SendFile(int out_fd,int in_fd,off64_t * offset,size_t count)195 bool SendFile(int out_fd, int in_fd, off64_t* offset, size_t count) {
196 while (count > 0) {
197 const auto bytes_written =
198 TEMP_FAILURE_RETRY(sendfile(out_fd, in_fd, offset, count));
199 if (bytes_written <= 0) {
200 return false;
201 }
202
203 count -= bytes_written;
204 }
205 return true;
206 }
207
208 } // namespace
209
Copy(const std::string & from,const std::string & to)210 bool Copy(const std::string& from, const std::string& to) {
211 android::base::unique_fd fd_from(
212 open(from.c_str(), O_RDONLY | O_CLOEXEC));
213 android::base::unique_fd fd_to(
214 open(to.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
215
216 if (fd_from.get() < 0 || fd_to.get() < 0) {
217 return false;
218 }
219
220 off_t farthest_seek = lseek(fd_from.get(), 0, SEEK_END);
221 if (farthest_seek == -1) {
222 PLOG(ERROR) << "Could not lseek in \"" << from << "\"";
223 return false;
224 }
225 if (ftruncate64(fd_to.get(), farthest_seek) < 0) {
226 PLOG(ERROR) << "Failed to ftruncate " << to;
227 }
228 off_t offset = 0;
229 while (offset < farthest_seek) {
230 off_t new_offset = lseek(fd_from.get(), offset, SEEK_HOLE);
231 if (new_offset == -1) {
232 // ENXIO is returned when there are no more blocks of this type
233 // coming.
234 if (errno == ENXIO) {
235 return true;
236 }
237 PLOG(ERROR) << "Could not lseek in \"" << from << "\"";
238 return false;
239 }
240 auto data_bytes = new_offset - offset;
241 if (lseek(fd_to.get(), offset, SEEK_SET) < 0) {
242 PLOG(ERROR) << "lseek() on " << to << " failed";
243 return false;
244 }
245 if (!SendFile(fd_to.get(), fd_from.get(), &offset, data_bytes)) {
246 PLOG(ERROR) << "sendfile() failed";
247 return false;
248 }
249 CHECK_EQ(offset, new_offset);
250 if (offset >= farthest_seek) {
251 return true;
252 }
253 new_offset = lseek(fd_from.get(), offset, SEEK_DATA);
254 if (new_offset == -1) {
255 // ENXIO is returned when there are no more blocks of this type
256 // coming.
257 if (errno == ENXIO) {
258 return true;
259 }
260 PLOG(ERROR) << "Could not lseek in \"" << from << "\"";
261 return false;
262 }
263 offset = new_offset;
264 }
265 return true;
266 }
267
AbsolutePath(const std::string & path)268 std::string AbsolutePath(const std::string& path) {
269 if (path.empty()) {
270 return {};
271 }
272 if (path[0] == '/') {
273 return path;
274 }
275 if (path[0] == '~') {
276 LOG(WARNING) << "Tilde expansion in path " << path <<" is not supported";
277 return {};
278 }
279
280 std::array<char, PATH_MAX> buffer{};
281 if (!realpath(".", buffer.data())) {
282 LOG(WARNING) << "Could not get real path for current directory \".\""
283 << ": " << strerror(errno);
284 return {};
285 }
286 return std::string{buffer.data()} + "/" + path;
287 }
288
FileSize(const std::string & path)289 off_t FileSize(const std::string& path) {
290 struct stat st {};
291 if (stat(path.c_str(), &st) == -1) {
292 return 0;
293 }
294 return st.st_size;
295 }
296
MakeFileExecutable(const std::string & path)297 bool MakeFileExecutable(const std::string& path) {
298 LOG(DEBUG) << "Making " << path << " executable";
299 return chmod(path.c_str(), S_IRWXU) == 0;
300 }
301
302 // TODO(schuffelen): Use std::filesystem::last_write_time when on C++17
FileModificationTime(const std::string & path)303 std::chrono::system_clock::time_point FileModificationTime(const std::string& path) {
304 struct stat st {};
305 if (stat(path.c_str(), &st) == -1) {
306 return std::chrono::system_clock::time_point();
307 }
308 std::chrono::seconds seconds(st.st_mtim.tv_sec);
309 return std::chrono::system_clock::time_point(seconds);
310 }
311
RenameFile(const std::string & current_filepath,const std::string & target_filepath)312 Result<std::string> RenameFile(const std::string& current_filepath,
313 const std::string& target_filepath) {
314 if (current_filepath != target_filepath) {
315 CF_EXPECT(rename(current_filepath.c_str(), target_filepath.c_str()) == 0,
316 "rename " << current_filepath << " to " << target_filepath
317 << " failed: " << strerror(errno));
318 }
319 return target_filepath;
320 }
321
RemoveFile(const std::string & file)322 bool RemoveFile(const std::string& file) {
323 LOG(DEBUG) << "Removing file " << file;
324 return remove(file.c_str()) == 0;
325 }
326
ReadFile(const std::string & file)327 std::string ReadFile(const std::string& file) {
328 std::string contents;
329 std::ifstream in(file, std::ios::in | std::ios::binary);
330 in.seekg(0, std::ios::end);
331 if (in.fail()) {
332 // TODO(schuffelen): Return a failing Result instead
333 return "";
334 }
335 if (in.tellg() == std::ifstream::pos_type(-1)) {
336 PLOG(ERROR) << "Failed to seek on " << file;
337 return "";
338 }
339 contents.resize(in.tellg());
340 in.seekg(0, std::ios::beg);
341 in.read(&contents[0], contents.size());
342 in.close();
343 return(contents);
344 }
345
CurrentDirectory()346 std::string CurrentDirectory() {
347 char* path = getcwd(nullptr, 0);
348 if (path == nullptr) {
349 PLOG(ERROR) << "`getcwd(nullptr, 0)` failed";
350 return "";
351 }
352 std::string ret(path);
353 free(path);
354 return ret;
355 }
356
SparseFileSizes(const std::string & path)357 FileSizes SparseFileSizes(const std::string& path) {
358 auto fd = SharedFD::Open(path, O_RDONLY);
359 if (!fd->IsOpen()) {
360 LOG(ERROR) << "Could not open \"" << path << "\": " << fd->StrError();
361 return {};
362 }
363 off_t farthest_seek = fd->LSeek(0, SEEK_END);
364 LOG(VERBOSE) << "Farthest seek: " << farthest_seek;
365 if (farthest_seek == -1) {
366 LOG(ERROR) << "Could not lseek in \"" << path << "\": " << fd->StrError();
367 return {};
368 }
369 off_t data_bytes = 0;
370 off_t offset = 0;
371 while (offset < farthest_seek) {
372 off_t new_offset = fd->LSeek(offset, SEEK_HOLE);
373 if (new_offset == -1) {
374 // ENXIO is returned when there are no more blocks of this type coming.
375 if (fd->GetErrno() == ENXIO) {
376 break;
377 } else {
378 LOG(ERROR) << "Could not lseek in \"" << path << "\": " << fd->StrError();
379 return {};
380 }
381 } else {
382 data_bytes += new_offset - offset;
383 offset = new_offset;
384 }
385 if (offset >= farthest_seek) {
386 break;
387 }
388 new_offset = fd->LSeek(offset, SEEK_DATA);
389 if (new_offset == -1) {
390 // ENXIO is returned when there are no more blocks of this type coming.
391 if (fd->GetErrno() == ENXIO) {
392 break;
393 } else {
394 LOG(ERROR) << "Could not lseek in \"" << path << "\": " << fd->StrError();
395 return {};
396 }
397 } else {
398 offset = new_offset;
399 }
400 }
401 return (FileSizes) { .sparse_size = farthest_seek, .disk_size = data_bytes };
402 }
403
cpp_basename(const std::string & str)404 std::string cpp_basename(const std::string& str) {
405 char* copy = strdup(str.c_str()); // basename may modify its argument
406 std::string ret(basename(copy));
407 free(copy);
408 return ret;
409 }
410
cpp_dirname(const std::string & str)411 std::string cpp_dirname(const std::string& str) {
412 char* copy = strdup(str.c_str()); // dirname may modify its argument
413 std::string ret(dirname(copy));
414 free(copy);
415 return ret;
416 }
417
FileIsSocket(const std::string & path)418 bool FileIsSocket(const std::string& path) {
419 struct stat st {};
420 return stat(path.c_str(), &st) == 0 && S_ISSOCK(st.st_mode);
421 }
422
GetDiskUsage(const std::string & path)423 int GetDiskUsage(const std::string& path) {
424 Command du_cmd("du");
425 du_cmd.AddParameter("-b");
426 du_cmd.AddParameter("-k");
427 du_cmd.AddParameter("-s");
428 du_cmd.AddParameter(path);
429 SharedFD read_fd;
430 SharedFD write_fd;
431 SharedFD::Pipe(&read_fd, &write_fd);
432 du_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, write_fd);
433 auto subprocess = du_cmd.Start();
434 std::array<char, 1024> text_output{};
435 const auto bytes_read = read_fd->Read(text_output.data(), text_output.size());
436 CHECK_GT(bytes_read, 0) << "Failed to read from pipe " << strerror(errno);
437 std::move(subprocess).Wait();
438 return atoi(text_output.data()) * 1024;
439 }
440
FindFile(const std::string & path,const std::string & target_name)441 std::string FindFile(const std::string& path, const std::string& target_name) {
442 std::string ret;
443 WalkDirectory(path,
444 [&ret, &target_name](const std::string& filename) mutable {
445 if (cpp_basename(filename) == target_name) {
446 ret = filename;
447 }
448 return true;
449 });
450 return ret;
451 }
452
453 // Recursively enumerate files in |dir|, and invoke the callback function with
454 // path to each file/directory.
WalkDirectory(const std::string & dir,const std::function<bool (const std::string &)> & callback)455 Result<void> WalkDirectory(
456 const std::string& dir,
457 const std::function<bool(const std::string&)>& callback) {
458 const auto files = CF_EXPECT(DirectoryContents(dir));
459 for (const auto& filename : files) {
460 if (filename == "." || filename == "..") {
461 continue;
462 }
463 auto file_path = dir + "/";
464 file_path.append(filename);
465 callback(file_path);
466 if (DirectoryExists(file_path)) {
467 WalkDirectory(file_path, callback);
468 }
469 }
470 return {};
471 }
472
473 class InotifyWatcher {
474 public:
InotifyWatcher(int inotify,const std::string & path,int watch_mode)475 InotifyWatcher(int inotify, const std::string& path, int watch_mode)
476 : inotify_(inotify) {
477 watch_ = inotify_add_watch(inotify_, path.c_str(), watch_mode);
478 }
~InotifyWatcher()479 virtual ~InotifyWatcher() { inotify_rm_watch(inotify_, watch_); }
480
481 private:
482 int inotify_;
483 int watch_;
484 };
485
WaitForFileInternal(const std::string & path,int timeoutSec,int inotify)486 static Result<void> WaitForFileInternal(const std::string& path, int timeoutSec,
487 int inotify) {
488 CF_EXPECT_NE(path, "", "Path is empty");
489
490 if (FileExists(path, true)) {
491 return {};
492 }
493
494 const auto targetTime =
495 std::chrono::system_clock::now() + std::chrono::seconds(timeoutSec);
496
497 const auto parentPath = cpp_dirname(path);
498 const auto filename = cpp_basename(path);
499
500 CF_EXPECT(WaitForFile(parentPath, timeoutSec),
501 "Error while waiting for parent directory creation");
502
503 auto watcher = InotifyWatcher(inotify, parentPath.c_str(), IN_CREATE);
504
505 if (FileExists(path, true)) {
506 return {};
507 }
508
509 while (true) {
510 const auto currentTime = std::chrono::system_clock::now();
511
512 if (currentTime >= targetTime) {
513 return CF_ERR("Timed out");
514 }
515
516 const auto timeRemain =
517 std::chrono::duration_cast<std::chrono::microseconds>(targetTime -
518 currentTime)
519 .count();
520 const auto secondInUsec =
521 std::chrono::microseconds(std::chrono::seconds(1)).count();
522 struct timeval timeout;
523
524 timeout.tv_sec = timeRemain / secondInUsec;
525 timeout.tv_usec = timeRemain % secondInUsec;
526
527 fd_set readfds;
528
529 FD_ZERO(&readfds);
530 FD_SET(inotify, &readfds);
531
532 auto ret = select(inotify + 1, &readfds, NULL, NULL, &timeout);
533
534 if (ret == 0) {
535 return CF_ERR("select() timed out");
536 } else if (ret < 0) {
537 return CF_ERRNO("select() failed");
538 }
539
540 auto names = GetCreatedFileListFromInotifyFd(inotify);
541
542 CF_EXPECT(names.size() > 0,
543 "Failed to get names from inotify " << strerror(errno));
544
545 if (Contains(names, filename)) {
546 return {};
547 }
548 }
549
550 return CF_ERR("This shouldn't be executed");
551 }
552
WaitForFile(const std::string & path,int timeoutSec)553 auto WaitForFile(const std::string& path, int timeoutSec)
554 -> decltype(WaitForFileInternal(path, timeoutSec, 0)) {
555 auto inotify = inotify_init1(IN_CLOEXEC);
556
557 ScopeGuard close_inotify([inotify]() { close(inotify); });
558
559 CF_EXPECT(WaitForFileInternal(path, timeoutSec, inotify));
560
561 return {};
562 }
563
WaitForUnixSocket(const std::string & path,int timeoutSec)564 Result<void> WaitForUnixSocket(const std::string& path, int timeoutSec) {
565 const auto targetTime =
566 std::chrono::system_clock::now() + std::chrono::seconds(timeoutSec);
567
568 CF_EXPECT(WaitForFile(path, timeoutSec),
569 "Waiting for socket path creation failed");
570 CF_EXPECT(FileIsSocket(path), "Specified path is not a socket");
571
572 while (true) {
573 const auto currentTime = std::chrono::system_clock::now();
574
575 if (currentTime >= targetTime) {
576 return CF_ERR("Timed out");
577 }
578
579 const auto timeRemain = std::chrono::duration_cast<std::chrono::seconds>(
580 targetTime - currentTime)
581 .count();
582 auto testConnect =
583 SharedFD::SocketLocalClient(path, false, SOCK_STREAM, timeRemain);
584
585 if (testConnect->IsOpen()) {
586 return {};
587 }
588
589 sched_yield();
590 }
591
592 return CF_ERR("This shouldn't be executed");
593 }
594
595 } // namespace cuttlefish
596