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
19 #ifdef __linux__
20 #include <linux/fiemap.h>
21 #include <linux/fs.h>
22 #include <sys/inotify.h>
23 #include <sys/sendfile.h>
24 #endif
25
26 #include <dirent.h>
27 #include <fcntl.h>
28 #include <ftw.h>
29 #include <libgen.h>
30 #include <sched.h>
31 #include <sys/ioctl.h>
32 #include <sys/select.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <sys/uio.h>
37 #include <unistd.h>
38
39 #include <algorithm>
40 #include <array>
41 #include <cerrno>
42 #include <chrono>
43 #include <climits>
44 #include <cstdio>
45 #include <cstdlib>
46 #include <cstring>
47 #include <fstream>
48 #include <ios>
49 #include <iosfwd>
50 #include <istream>
51 #include <memory>
52 #include <numeric>
53 #include <ostream>
54 #include <ratio>
55 #include <regex>
56 #include <string>
57 #include <vector>
58
59 #include <android-base/file.h>
60 #include <android-base/logging.h>
61 #include <android-base/macros.h>
62 #include <android-base/strings.h>
63 #include <android-base/unique_fd.h>
64
65 #include "common/libs/fs/shared_buf.h"
66 #include "common/libs/fs/shared_fd.h"
67 #include "common/libs/utils/contains.h"
68 #include "common/libs/utils/inotify.h"
69 #include "common/libs/utils/result.h"
70 #include "common/libs/utils/subprocess.h"
71 #include "common/libs/utils/users.h"
72
73 #ifdef __APPLE__
74 #define off64_t off_t
75 #define ftruncate64 ftruncate
76 #endif
77
78 namespace cuttlefish {
79
FileExists(const std::string & path,bool follow_symlinks)80 bool FileExists(const std::string& path, bool follow_symlinks) {
81 struct stat st {};
82 return (follow_symlinks ? stat : lstat)(path.c_str(), &st) == 0;
83 }
84
FileDeviceId(const std::string & path)85 Result<dev_t> FileDeviceId(const std::string& path) {
86 struct stat out;
87 CF_EXPECTF(
88 stat(path.c_str(), &out) == 0,
89 "stat() failed trying to retrieve device ID information for \"{}\" "
90 "with error: {}",
91 path, strerror(errno));
92 return out.st_dev;
93 }
94
CanHardLink(const std::string & source,const std::string & destination)95 Result<bool> CanHardLink(const std::string& source,
96 const std::string& destination) {
97 return CF_EXPECT(FileDeviceId(source)) ==
98 CF_EXPECT(FileDeviceId(destination));
99 }
100
FileInodeNumber(const std::string & path)101 Result<ino_t> FileInodeNumber(const std::string& path) {
102 struct stat out;
103 CF_EXPECTF(
104 stat(path.c_str(), &out) == 0,
105 "stat() failed trying to retrieve inode num information for \"{}\" "
106 "with error: {}",
107 path, strerror(errno));
108 return out.st_ino;
109 }
110
AreHardLinked(const std::string & source,const std::string & destination)111 Result<bool> AreHardLinked(const std::string& source,
112 const std::string& destination) {
113 return (CF_EXPECT(FileDeviceId(source)) ==
114 CF_EXPECT(FileDeviceId(destination))) &&
115 (CF_EXPECT(FileInodeNumber(source)) ==
116 CF_EXPECT(FileInodeNumber(destination)));
117 }
118
CreateHardLink(const std::string & target,const std::string & hardlink,const bool overwrite_existing)119 Result<std::string> CreateHardLink(const std::string& target,
120 const std::string& hardlink,
121 const bool overwrite_existing) {
122 if (FileExists(hardlink)) {
123 if (CF_EXPECT(AreHardLinked(target, hardlink))) {
124 return hardlink;
125 }
126 if (!overwrite_existing) {
127 return CF_ERRF(
128 "Cannot hardlink from \"{}\" to \"{}\", the second file already "
129 "exists and is not hardlinked to the first",
130 target, hardlink);
131 }
132 LOG(WARNING) << "Overwriting existing file \"" << hardlink << "\" with \""
133 << target << "\" from the cache";
134 CF_EXPECTF(unlink(hardlink.c_str()) == 0,
135 "Failed to unlink \"{}\" with error: {}", hardlink,
136 strerror(errno));
137 }
138 CF_EXPECTF(link(target.c_str(), hardlink.c_str()) == 0,
139 "link() failed trying to create hardlink from \"{}\" to \"{}\" "
140 "with error: {}",
141 target, hardlink, strerror(errno));
142 return hardlink;
143 }
144
FileHasContent(const std::string & path)145 bool FileHasContent(const std::string& path) {
146 return FileSize(path) > 0;
147 }
148
DirectoryContents(const std::string & path)149 Result<std::vector<std::string>> DirectoryContents(const std::string& path) {
150 std::vector<std::string> ret;
151 std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir);
152 CF_EXPECTF(dir != nullptr, "Could not read from dir \"{}\"", path);
153 struct dirent* ent{};
154 while ((ent = readdir(dir.get()))) {
155 ret.emplace_back(ent->d_name);
156 }
157 return ret;
158 }
159
DirectoryExists(const std::string & path,bool follow_symlinks)160 bool DirectoryExists(const std::string& path, bool follow_symlinks) {
161 struct stat st {};
162 if ((follow_symlinks ? stat : lstat)(path.c_str(), &st) == -1) {
163 return false;
164 }
165 if ((st.st_mode & S_IFMT) != S_IFDIR) {
166 return false;
167 }
168 return true;
169 }
170
EnsureDirectoryExists(const std::string & directory_path,const mode_t mode,const std::string & group_name)171 Result<void> EnsureDirectoryExists(const std::string& directory_path,
172 const mode_t mode,
173 const std::string& group_name) {
174 if (DirectoryExists(directory_path, /* follow_symlinks */ true)) {
175 return {};
176 }
177 if (FileExists(directory_path, false) && !FileExists(directory_path, true)) {
178 // directory_path is a link to a path that doesn't exist. This could happen
179 // after executing certain cvd subcommands.
180 CF_EXPECT(RemoveFile(directory_path),
181 "Can't remove broken link: " << directory_path);
182 }
183 const auto parent_dir = android::base::Dirname(directory_path);
184 if (parent_dir.size() > 1) {
185 EnsureDirectoryExists(parent_dir, mode, group_name);
186 }
187 LOG(VERBOSE) << "Setting up " << directory_path;
188 if (mkdir(directory_path.c_str(), mode) < 0 && errno != EEXIST) {
189 return CF_ERRNO("Failed to create directory: \"" << directory_path << "\""
190 << strerror(errno));
191 }
192
193 CF_EXPECTF(chmod(directory_path.c_str(), mode) == 0,
194 "Failed to set permission on {}: {}", directory_path,
195 strerror(errno));
196
197 if (group_name != "") {
198 CF_EXPECT(ChangeGroup(directory_path, group_name));
199 }
200
201 return {};
202 }
203
ChangeGroup(const std::string & path,const std::string & group_name)204 Result<void> ChangeGroup(const std::string& path,
205 const std::string& group_name) {
206 auto groupId = GroupIdFromName(group_name);
207
208 if (groupId == -1) {
209 return CF_ERR("Failed to get group id: ") << group_name;
210 }
211
212 if (chown(path.c_str(), -1, groupId) != 0) {
213 return CF_ERRNO("Failed to set group for path: "
214 << path << ", " << group_name << ", " << strerror(errno));
215 }
216
217 return {};
218 }
219
CanAccess(const std::string & path,const int mode)220 bool CanAccess(const std::string& path, const int mode) {
221 return access(path.c_str(), mode) == 0;
222 }
223
IsDirectoryEmpty(const std::string & path)224 bool IsDirectoryEmpty(const std::string& path) {
225 auto direc = ::opendir(path.c_str());
226 if (!direc) {
227 LOG(ERROR) << "IsDirectoryEmpty test failed with " << path
228 << " as it failed to be open" << std::endl;
229 return false;
230 }
231
232 decltype(::readdir(direc)) sub = nullptr;
233 int cnt {0};
234 while ( (sub = ::readdir(direc)) ) {
235 cnt++;
236 if (cnt > 2) {
237 LOG(ERROR) << "IsDirectoryEmpty test failed with " << path
238 << " as it exists but not empty" << std::endl;
239 return false;
240 }
241 }
242 return true;
243 }
244
RecursivelyRemoveDirectory(const std::string & path)245 Result<void> RecursivelyRemoveDirectory(const std::string& path) {
246 // Copied from libbase TemporaryDir destructor.
247 auto callback = [](const char* child, const struct stat*, int file_type,
248 struct FTW*) -> int {
249 switch (file_type) {
250 case FTW_D:
251 case FTW_DP:
252 case FTW_DNR:
253 if (rmdir(child) == -1) {
254 PLOG(ERROR) << "rmdir " << child;
255 return -1;
256 }
257 break;
258 case FTW_NS:
259 default:
260 if (rmdir(child) != -1) {
261 break;
262 }
263 // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
264 FALLTHROUGH_INTENDED;
265 case FTW_F:
266 case FTW_SL:
267 case FTW_SLN:
268 if (unlink(child) == -1) {
269 PLOG(ERROR) << "unlink " << child;
270 return -1;
271 }
272 break;
273 }
274 return 0;
275 };
276
277 if (nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) < 0) {
278 return CF_ERRNO("Failed to remove directory \""
279 << path << "\": " << strerror(errno));
280 }
281 return {};
282 }
283
284 namespace {
285
SendFile(int out_fd,int in_fd,off64_t * offset,size_t count)286 bool SendFile(int out_fd, int in_fd, off64_t* offset, size_t count) {
287 while (count > 0) {
288 #ifdef __linux__
289 const auto bytes_written =
290 TEMP_FAILURE_RETRY(sendfile(out_fd, in_fd, offset, count));
291 if (bytes_written <= 0) {
292 return false;
293 }
294 #elif defined(__APPLE__)
295 off_t bytes_written = count;
296 auto success = TEMP_FAILURE_RETRY(
297 sendfile(in_fd, out_fd, *offset, &bytes_written, nullptr, 0));
298 *offset += bytes_written;
299 if (success < 0 || bytes_written == 0) {
300 return false;
301 }
302 #endif
303 count -= bytes_written;
304 }
305 return true;
306 }
307
308 } // namespace
309
Copy(const std::string & from,const std::string & to)310 bool Copy(const std::string& from, const std::string& to) {
311 android::base::unique_fd fd_from(
312 open(from.c_str(), O_RDONLY | O_CLOEXEC));
313 android::base::unique_fd fd_to(
314 open(to.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
315
316 if (fd_from.get() < 0 || fd_to.get() < 0) {
317 return false;
318 }
319
320 off_t farthest_seek = lseek(fd_from.get(), 0, SEEK_END);
321 if (farthest_seek == -1) {
322 PLOG(ERROR) << "Could not lseek in \"" << from << "\"";
323 return false;
324 }
325 if (ftruncate64(fd_to.get(), farthest_seek) < 0) {
326 PLOG(ERROR) << "Failed to ftruncate " << to;
327 }
328 off_t offset = 0;
329 while (offset < farthest_seek) {
330 off_t new_offset = lseek(fd_from.get(), offset, SEEK_HOLE);
331 if (new_offset == -1) {
332 // ENXIO is returned when there are no more blocks of this type
333 // coming.
334 if (errno == ENXIO) {
335 return true;
336 }
337 PLOG(ERROR) << "Could not lseek in \"" << from << "\"";
338 return false;
339 }
340 auto data_bytes = new_offset - offset;
341 if (lseek(fd_to.get(), offset, SEEK_SET) < 0) {
342 PLOG(ERROR) << "lseek() on " << to << " failed";
343 return false;
344 }
345 if (!SendFile(fd_to.get(), fd_from.get(), &offset, data_bytes)) {
346 PLOG(ERROR) << "sendfile() failed";
347 return false;
348 }
349 CHECK_EQ(offset, new_offset);
350 if (offset >= farthest_seek) {
351 return true;
352 }
353 new_offset = lseek(fd_from.get(), offset, SEEK_DATA);
354 if (new_offset == -1) {
355 // ENXIO is returned when there are no more blocks of this type
356 // coming.
357 if (errno == ENXIO) {
358 return true;
359 }
360 PLOG(ERROR) << "Could not lseek in \"" << from << "\"";
361 return false;
362 }
363 offset = new_offset;
364 }
365 return true;
366 }
367
AbsolutePath(const std::string & path)368 std::string AbsolutePath(const std::string& path) {
369 if (path.empty()) {
370 return {};
371 }
372 if (path[0] == '/') {
373 return path;
374 }
375 if (path[0] == '~') {
376 LOG(WARNING) << "Tilde expansion in path " << path <<" is not supported";
377 return {};
378 }
379
380 std::array<char, PATH_MAX> buffer{};
381 if (!realpath(".", buffer.data())) {
382 LOG(WARNING) << "Could not get real path for current directory \".\""
383 << ": " << strerror(errno);
384 return {};
385 }
386 return std::string{buffer.data()} + "/" + path;
387 }
388
FileSize(const std::string & path)389 off_t FileSize(const std::string& path) {
390 struct stat st {};
391 if (stat(path.c_str(), &st) == -1) {
392 return 0;
393 }
394 return st.st_size;
395 }
396
MakeFileExecutable(const std::string & path)397 bool MakeFileExecutable(const std::string& path) {
398 LOG(DEBUG) << "Making " << path << " executable";
399 return chmod(path.c_str(), S_IRWXU) == 0;
400 }
401
402 // TODO(schuffelen): Use std::filesystem::last_write_time when on C++17
FileModificationTime(const std::string & path)403 std::chrono::system_clock::time_point FileModificationTime(const std::string& path) {
404 struct stat st {};
405 if (stat(path.c_str(), &st) == -1) {
406 return std::chrono::system_clock::time_point();
407 }
408 #ifdef __linux__
409 std::chrono::seconds seconds(st.st_mtim.tv_sec);
410 #elif defined(__APPLE__)
411 std::chrono::seconds seconds(st.st_mtimespec.tv_sec);
412 #else
413 #error "Unsupported operating system"
414 #endif
415 return std::chrono::system_clock::time_point(seconds);
416 }
417
RenameFile(const std::string & current_filepath,const std::string & target_filepath)418 Result<std::string> RenameFile(const std::string& current_filepath,
419 const std::string& target_filepath) {
420 if (current_filepath != target_filepath) {
421 CF_EXPECT(rename(current_filepath.c_str(), target_filepath.c_str()) == 0,
422 "rename " << current_filepath << " to " << target_filepath
423 << " failed: " << strerror(errno));
424 }
425 return target_filepath;
426 }
427
RemoveFile(const std::string & file)428 bool RemoveFile(const std::string& file) {
429 LOG(DEBUG) << "Removing file " << file;
430 if (remove(file.c_str()) == 0) {
431 return true;
432 }
433 LOG(ERROR) << "Failed to remove file " << file << " : "
434 << std::strerror(errno);
435 return false;
436 }
437
ReadFile(const std::string & file)438 std::string ReadFile(const std::string& file) {
439 std::string contents;
440 std::ifstream in(file, std::ios::in | std::ios::binary);
441 in.seekg(0, std::ios::end);
442 if (in.fail()) {
443 // TODO(schuffelen): Return a failing Result instead
444 return "";
445 }
446 if (in.tellg() == std::ifstream::pos_type(-1)) {
447 PLOG(ERROR) << "Failed to seek on " << file;
448 return "";
449 }
450 contents.resize(in.tellg());
451 in.seekg(0, std::ios::beg);
452 in.read(&contents[0], contents.size());
453 in.close();
454 return(contents);
455 }
456
ReadFileContents(const std::string & filepath)457 Result<std::string> ReadFileContents(const std::string& filepath) {
458 CF_EXPECTF(FileExists(filepath), "The file at \"{}\" does not exist.",
459 filepath);
460 auto file = SharedFD::Open(filepath, O_RDONLY);
461 CF_EXPECTF(file->IsOpen(), "Failed to open file \"{}\". Error:\n", filepath,
462 file->StrError());
463 std::string file_content;
464 auto size = ReadAll(file, &file_content);
465 CF_EXPECTF(size >= 0, "Failed to read file contents. Error:\n",
466 file->StrError());
467 return file_content;
468 }
469
CurrentDirectory()470 std::string CurrentDirectory() {
471 std::unique_ptr<char, void (*)(void*)> cwd(getcwd(nullptr, 0), &free);
472 std::string process_cwd(cwd.get());
473 if (!cwd) {
474 PLOG(ERROR) << "`getcwd(nullptr, 0)` failed";
475 return "";
476 }
477 return process_cwd;
478 }
479
SparseFileSizes(const std::string & path)480 FileSizes SparseFileSizes(const std::string& path) {
481 auto fd = SharedFD::Open(path, O_RDONLY);
482 if (!fd->IsOpen()) {
483 LOG(ERROR) << "Could not open \"" << path << "\": " << fd->StrError();
484 return {};
485 }
486 off_t farthest_seek = fd->LSeek(0, SEEK_END);
487 LOG(VERBOSE) << "Farthest seek: " << farthest_seek;
488 if (farthest_seek == -1) {
489 LOG(ERROR) << "Could not lseek in \"" << path << "\": " << fd->StrError();
490 return {};
491 }
492 off_t data_bytes = 0;
493 off_t offset = 0;
494 while (offset < farthest_seek) {
495 off_t new_offset = fd->LSeek(offset, SEEK_HOLE);
496 if (new_offset == -1) {
497 // ENXIO is returned when there are no more blocks of this type coming.
498 if (fd->GetErrno() == ENXIO) {
499 break;
500 } else {
501 LOG(ERROR) << "Could not lseek in \"" << path << "\": " << fd->StrError();
502 return {};
503 }
504 } else {
505 data_bytes += new_offset - offset;
506 offset = new_offset;
507 }
508 if (offset >= farthest_seek) {
509 break;
510 }
511 new_offset = fd->LSeek(offset, SEEK_DATA);
512 if (new_offset == -1) {
513 // ENXIO is returned when there are no more blocks of this type coming.
514 if (fd->GetErrno() == ENXIO) {
515 break;
516 } else {
517 LOG(ERROR) << "Could not lseek in \"" << path << "\": " << fd->StrError();
518 return {};
519 }
520 } else {
521 offset = new_offset;
522 }
523 }
524 return (FileSizes) { .sparse_size = farthest_seek, .disk_size = data_bytes };
525 }
526
cpp_basename(const std::string & str)527 std::string cpp_basename(const std::string& str) {
528 char* copy = strdup(str.c_str()); // basename may modify its argument
529 std::string ret(basename(copy));
530 free(copy);
531 return ret;
532 }
533
cpp_dirname(const std::string & str)534 std::string cpp_dirname(const std::string& str) {
535 return android::base::Dirname(str);
536 }
537
FileIsSocket(const std::string & path)538 bool FileIsSocket(const std::string& path) {
539 struct stat st {};
540 return stat(path.c_str(), &st) == 0 && S_ISSOCK(st.st_mode);
541 }
542
GetDiskUsage(const std::string & path)543 int GetDiskUsage(const std::string& path) {
544 Command du_cmd("du");
545 du_cmd.AddParameter("-b");
546 du_cmd.AddParameter("-k");
547 du_cmd.AddParameter("-s");
548 du_cmd.AddParameter(path);
549 SharedFD read_fd;
550 SharedFD write_fd;
551 SharedFD::Pipe(&read_fd, &write_fd);
552 du_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, write_fd);
553 auto subprocess = du_cmd.Start();
554 std::array<char, 1024> text_output{};
555 const auto bytes_read = read_fd->Read(text_output.data(), text_output.size());
556 CHECK_GT(bytes_read, 0) << "Failed to read from pipe " << strerror(errno);
557 std::move(subprocess).Wait();
558 return atoi(text_output.data()) * 1024;
559 }
560
561 /**
562 * Find an image file through the input path and pattern.
563 *
564 * If it finds the file, return the path string.
565 * If it can't find the file, return empty string.
566 */
FindImage(const std::string & search_path,const std::vector<std::string> & pattern)567 std::string FindImage(const std::string& search_path,
568 const std::vector<std::string>& pattern) {
569 const std::string& search_path_extend = search_path + "/";
570 for (const auto& name : pattern) {
571 std::string image = search_path_extend + name;
572 if (FileExists(image)) {
573 return image;
574 }
575 }
576 return "";
577 }
578
FindFile(const std::string & path,const std::string & target_name)579 std::string FindFile(const std::string& path, const std::string& target_name) {
580 std::string ret;
581 WalkDirectory(path,
582 [&ret, &target_name](const std::string& filename) mutable {
583 if (cpp_basename(filename) == target_name) {
584 ret = filename;
585 }
586 return true;
587 });
588 return ret;
589 }
590
591 // Recursively enumerate files in |dir|, and invoke the callback function with
592 // path to each file/directory.
WalkDirectory(const std::string & dir,const std::function<bool (const std::string &)> & callback)593 Result<void> WalkDirectory(
594 const std::string& dir,
595 const std::function<bool(const std::string&)>& callback) {
596 const auto files = CF_EXPECT(DirectoryContents(dir));
597 for (const auto& filename : files) {
598 if (filename == "." || filename == "..") {
599 continue;
600 }
601 auto file_path = dir + "/";
602 file_path.append(filename);
603 callback(file_path);
604 if (DirectoryExists(file_path)) {
605 WalkDirectory(file_path, callback);
606 }
607 }
608 return {};
609 }
610
611 #ifdef __linux__
612 class InotifyWatcher {
613 public:
InotifyWatcher(int inotify,const std::string & path,int watch_mode)614 InotifyWatcher(int inotify, const std::string& path, int watch_mode)
615 : inotify_(inotify) {
616 watch_ = inotify_add_watch(inotify_, path.c_str(), watch_mode);
617 }
~InotifyWatcher()618 virtual ~InotifyWatcher() { inotify_rm_watch(inotify_, watch_); }
619
620 private:
621 int inotify_;
622 int watch_;
623 };
624
WaitForFileInternal(const std::string & path,int timeoutSec,int inotify)625 static Result<void> WaitForFileInternal(const std::string& path, int timeoutSec,
626 int inotify) {
627 CF_EXPECT_NE(path, "", "Path is empty");
628
629 if (FileExists(path, true)) {
630 return {};
631 }
632
633 const auto targetTime =
634 std::chrono::system_clock::now() + std::chrono::seconds(timeoutSec);
635
636 const auto parentPath = cpp_dirname(path);
637 const auto filename = cpp_basename(path);
638
639 CF_EXPECT(WaitForFile(parentPath, timeoutSec),
640 "Error while waiting for parent directory creation");
641
642 auto watcher = InotifyWatcher(inotify, parentPath.c_str(), IN_CREATE);
643
644 if (FileExists(path, true)) {
645 return {};
646 }
647
648 while (true) {
649 const auto currentTime = std::chrono::system_clock::now();
650
651 if (currentTime >= targetTime) {
652 return CF_ERR("Timed out");
653 }
654
655 const auto timeRemain =
656 std::chrono::duration_cast<std::chrono::microseconds>(targetTime -
657 currentTime)
658 .count();
659 const auto secondInUsec =
660 std::chrono::microseconds(std::chrono::seconds(1)).count();
661 struct timeval timeout;
662
663 timeout.tv_sec = timeRemain / secondInUsec;
664 timeout.tv_usec = timeRemain % secondInUsec;
665
666 fd_set readfds;
667
668 FD_ZERO(&readfds);
669 FD_SET(inotify, &readfds);
670
671 auto ret = select(inotify + 1, &readfds, NULL, NULL, &timeout);
672
673 if (ret == 0) {
674 return CF_ERR("select() timed out");
675 } else if (ret < 0) {
676 return CF_ERRNO("select() failed");
677 }
678
679 auto names = GetCreatedFileListFromInotifyFd(inotify);
680
681 CF_EXPECT(names.size() > 0,
682 "Failed to get names from inotify " << strerror(errno));
683
684 if (Contains(names, filename)) {
685 return {};
686 }
687 }
688
689 return CF_ERR("This shouldn't be executed");
690 }
691
WaitForFile(const std::string & path,int timeoutSec)692 auto WaitForFile(const std::string& path, int timeoutSec)
693 -> decltype(WaitForFileInternal(path, timeoutSec, 0)) {
694 android::base::unique_fd inotify(inotify_init1(IN_CLOEXEC));
695
696 CF_EXPECT(WaitForFileInternal(path, timeoutSec, inotify.get()));
697
698 return {};
699 }
700
WaitForUnixSocket(const std::string & path,int timeoutSec)701 Result<void> WaitForUnixSocket(const std::string& path, int timeoutSec) {
702 const auto targetTime =
703 std::chrono::system_clock::now() + std::chrono::seconds(timeoutSec);
704
705 CF_EXPECT(WaitForFile(path, timeoutSec),
706 "Waiting for socket path creation failed");
707 CF_EXPECT(FileIsSocket(path), "Specified path is not a socket");
708
709 while (true) {
710 const auto currentTime = std::chrono::system_clock::now();
711
712 if (currentTime >= targetTime) {
713 return CF_ERR("Timed out");
714 }
715
716 const auto timeRemain = std::chrono::duration_cast<std::chrono::seconds>(
717 targetTime - currentTime)
718 .count();
719 auto testConnect =
720 SharedFD::SocketLocalClient(path, false, SOCK_STREAM, timeRemain);
721
722 if (testConnect->IsOpen()) {
723 return {};
724 }
725
726 sched_yield();
727 }
728
729 return CF_ERR("This shouldn't be executed");
730 }
731
WaitForUnixSocketListeningWithoutConnect(const std::string & path,int timeoutSec)732 Result<void> WaitForUnixSocketListeningWithoutConnect(const std::string& path,
733 int timeoutSec) {
734 const auto targetTime =
735 std::chrono::system_clock::now() + std::chrono::seconds(timeoutSec);
736
737 CF_EXPECT(WaitForFile(path, timeoutSec),
738 "Waiting for socket path creation failed");
739 CF_EXPECT(FileIsSocket(path), "Specified path is not a socket");
740
741 std::regex socket_state_regex("TST=(.*)");
742
743 while (true) {
744 const auto currentTime = std::chrono::system_clock::now();
745
746 if (currentTime >= targetTime) {
747 return CF_ERR("Timed out");
748 }
749
750 Command lsof("lsof");
751 lsof.AddParameter(/*"format"*/ "-F", /*"connection state"*/ "TST");
752 lsof.AddParameter(path);
753 std::string lsof_out;
754 std::string lsof_err;
755 int rval =
756 RunWithManagedStdio(std::move(lsof), nullptr, &lsof_out, &lsof_err);
757 if (rval != 0) {
758 return CF_ERR("Failed to run `lsof`, stderr: " << lsof_err);
759 }
760
761 LOG(DEBUG) << "lsof stdout:|" << lsof_out << "|";
762 LOG(DEBUG) << "lsof stderr:|" << lsof_err << "|";
763
764 std::smatch socket_state_match;
765 if (std::regex_search(lsof_out, socket_state_match, socket_state_regex)) {
766 if (socket_state_match.size() == 2) {
767 const std::string& socket_state = socket_state_match[1];
768 if (socket_state == "LISTEN") {
769 return {};
770 }
771 }
772 }
773
774 sched_yield();
775 }
776
777 return CF_ERR("This shouldn't be executed");
778 }
779 #endif
780
781 namespace {
782
FoldPath(std::vector<std::string> elements,std::string token)783 std::vector<std::string> FoldPath(std::vector<std::string> elements,
784 std::string token) {
785 static constexpr std::array kIgnored = {".", "..", ""};
786 if (token == ".." && !elements.empty()) {
787 elements.pop_back();
788 } else if (!Contains(kIgnored, token)) {
789 elements.emplace_back(token);
790 }
791 return elements;
792 }
793
CalculatePrefix(const InputPathForm & path_info)794 Result<std::vector<std::string>> CalculatePrefix(
795 const InputPathForm& path_info) {
796 const auto& path = path_info.path_to_convert;
797 std::string working_dir;
798 if (path_info.current_working_dir) {
799 working_dir = *path_info.current_working_dir;
800 } else {
801 working_dir = CurrentDirectory();
802 }
803 std::vector<std::string> prefix;
804 if (path == "~" || android::base::StartsWith(path, "~/")) {
805 const auto home_dir =
806 path_info.home_dir.value_or(CF_EXPECT(SystemWideUserHome()));
807 prefix = android::base::Tokenize(home_dir, "/");
808 } else if (!android::base::StartsWith(path, "/")) {
809 prefix = android::base::Tokenize(working_dir, "/");
810 }
811 return prefix;
812 }
813
814 } // namespace
815
EmulateAbsolutePath(const InputPathForm & path_info)816 Result<std::string> EmulateAbsolutePath(const InputPathForm& path_info) {
817 const auto& path = path_info.path_to_convert;
818 std::string working_dir;
819 if (path_info.current_working_dir) {
820 working_dir = *path_info.current_working_dir;
821 } else {
822 working_dir = CurrentDirectory();
823 }
824 CF_EXPECT(android::base::StartsWith(working_dir, '/'),
825 "Current working directory should be given in an absolute path.");
826
827 if (path.empty()) {
828 LOG(ERROR) << "The requested path to convert an absolute path is empty.";
829 return "";
830 }
831
832 auto prefix = CF_EXPECT(CalculatePrefix(path_info));
833 std::vector<std::string> components;
834 components.insert(components.end(), prefix.begin(), prefix.end());
835 auto tokens = android::base::Tokenize(path, "/");
836 // remove first ~
837 if (!tokens.empty() && tokens.at(0) == "~") {
838 tokens.erase(tokens.begin());
839 }
840 components.insert(components.end(), tokens.begin(), tokens.end());
841
842 std::string combined = android::base::Join(components, "/");
843 CF_EXPECTF(!Contains(components, "~"),
844 "~ is not allowed in the middle of the path: {}", combined);
845
846 auto processed_tokens = std::accumulate(components.begin(), components.end(),
847 std::vector<std::string>{}, FoldPath);
848
849 const auto processed_path = "/" + android::base::Join(processed_tokens, "/");
850
851 std::string real_path = processed_path;
852 if (path_info.follow_symlink && FileExists(processed_path)) {
853 CF_EXPECTF(android::base::Realpath(processed_path, &real_path),
854 "Failed to effectively conduct readpath -f {}", processed_path);
855 }
856 return real_path;
857 }
858
859 } // namespace cuttlefish
860