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