• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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