John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 1 | #include "aos/util/file.h" |
Brian Silverman | 61175fb | 2016-03-13 15:35:56 -0400 | [diff] [blame] | 2 | |
| 3 | #include <fcntl.h> |
Austin Schuh | e991fe2 | 2020-11-18 16:53:39 -0800 | [diff] [blame] | 4 | #include <fts.h> |
Austin Schuh | fccb2d0 | 2020-01-26 16:11:19 -0800 | [diff] [blame] | 5 | #include <sys/stat.h> |
| 6 | #include <sys/types.h> |
Brian Silverman | 61175fb | 2016-03-13 15:35:56 -0400 | [diff] [blame] | 7 | #include <unistd.h> |
| 8 | |
James Kuszmaul | 3ae4226 | 2019-11-08 12:33:41 -0800 | [diff] [blame] | 9 | #include <string_view> |
| 10 | |
John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 11 | #include "aos/scoped/scoped_fd.h" |
Brian Silverman | 61175fb | 2016-03-13 15:35:56 -0400 | [diff] [blame] | 12 | |
| 13 | namespace aos { |
| 14 | namespace util { |
| 15 | |
James Kuszmaul | 3ae4226 | 2019-11-08 12:33:41 -0800 | [diff] [blame] | 16 | ::std::string ReadFileToStringOrDie(const std::string_view filename) { |
Brian Silverman | 61175fb | 2016-03-13 15:35:56 -0400 | [diff] [blame] | 17 | ::std::string r; |
Austin Schuh | cb10841 | 2019-10-13 16:09:54 -0700 | [diff] [blame] | 18 | ScopedFD fd(open(::std::string(filename).c_str(), O_RDONLY)); |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 19 | PCHECK(fd.get() != -1) << ": opening " << filename; |
Brian Silverman | 61175fb | 2016-03-13 15:35:56 -0400 | [diff] [blame] | 20 | while (true) { |
| 21 | char buffer[1024]; |
| 22 | const ssize_t result = read(fd.get(), buffer, sizeof(buffer)); |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 23 | PCHECK(result >= 0) << ": reading from " << filename; |
| 24 | if (result == 0) { |
Brian Silverman | 61175fb | 2016-03-13 15:35:56 -0400 | [diff] [blame] | 25 | break; |
| 26 | } |
| 27 | r.append(buffer, result); |
| 28 | } |
| 29 | return r; |
| 30 | } |
| 31 | |
James Kuszmaul | 3ae4226 | 2019-11-08 12:33:41 -0800 | [diff] [blame] | 32 | void WriteStringToFileOrDie(const std::string_view filename, |
Austin Schuh | e3fc053 | 2021-02-07 22:14:22 -0800 | [diff] [blame^] | 33 | const std::string_view contents, |
| 34 | mode_t permissions) { |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 35 | ::std::string r; |
| 36 | ScopedFD fd(open(::std::string(filename).c_str(), |
Austin Schuh | e3fc053 | 2021-02-07 22:14:22 -0800 | [diff] [blame^] | 37 | O_CREAT | O_WRONLY | O_TRUNC, permissions)); |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 38 | PCHECK(fd.get() != -1) << ": opening " << filename; |
| 39 | size_t size_written = 0; |
| 40 | while (size_written != contents.size()) { |
| 41 | const ssize_t result = write(fd.get(), contents.data() + size_written, |
| 42 | contents.size() - size_written); |
| 43 | PCHECK(result >= 0) << ": reading from " << filename; |
| 44 | if (result == 0) { |
| 45 | break; |
| 46 | } |
| 47 | |
| 48 | size_written += result; |
| 49 | } |
| 50 | } |
| 51 | |
Brian Silverman | a9f2ec9 | 2020-10-06 18:00:53 -0700 | [diff] [blame] | 52 | bool MkdirPIfSpace(std::string_view path, mode_t mode) { |
Austin Schuh | fccb2d0 | 2020-01-26 16:11:19 -0800 | [diff] [blame] | 53 | auto last_slash_pos = path.find_last_of("/"); |
| 54 | |
| 55 | std::string folder(last_slash_pos == std::string_view::npos |
| 56 | ? std::string_view("") |
| 57 | : path.substr(0, last_slash_pos)); |
Brian Silverman | a9f2ec9 | 2020-10-06 18:00:53 -0700 | [diff] [blame] | 58 | if (folder.empty()) { |
| 59 | return true; |
| 60 | } |
| 61 | if (!MkdirPIfSpace(folder, mode)) { |
| 62 | return false; |
| 63 | } |
Austin Schuh | fccb2d0 | 2020-01-26 16:11:19 -0800 | [diff] [blame] | 64 | const int result = mkdir(folder.c_str(), mode); |
| 65 | if (result == -1 && errno == EEXIST) { |
| 66 | VLOG(2) << folder << " already exists"; |
Brian Silverman | a9f2ec9 | 2020-10-06 18:00:53 -0700 | [diff] [blame] | 67 | return true; |
| 68 | } else if (result == -1 && errno == ENOSPC) { |
| 69 | VLOG(2) << "Out of space"; |
| 70 | return false; |
Austin Schuh | fccb2d0 | 2020-01-26 16:11:19 -0800 | [diff] [blame] | 71 | } else { |
| 72 | VLOG(1) << "Created " << folder; |
| 73 | } |
| 74 | PCHECK(result == 0) << ": Error creating " << folder; |
Brian Silverman | a9f2ec9 | 2020-10-06 18:00:53 -0700 | [diff] [blame] | 75 | return true; |
Austin Schuh | fccb2d0 | 2020-01-26 16:11:19 -0800 | [diff] [blame] | 76 | } |
| 77 | |
James Kuszmaul | f817809 | 2020-05-10 18:46:45 -0700 | [diff] [blame] | 78 | bool PathExists(std::string_view path) { |
| 79 | struct stat buffer; |
| 80 | return stat(path.data(), &buffer) == 0; |
| 81 | } |
| 82 | |
Austin Schuh | e991fe2 | 2020-11-18 16:53:39 -0800 | [diff] [blame] | 83 | void UnlinkRecursive(std::string_view path) { |
| 84 | FTS *ftsp = NULL; |
| 85 | FTSENT *curr; |
| 86 | |
| 87 | // Cast needed (in C) because fts_open() takes a "char * const *", instead |
| 88 | // of a "const char * const *", which is only allowed in C++. fts_open() |
| 89 | // does not modify the argument. |
| 90 | std::string p(path); |
| 91 | char *files[] = {const_cast<char *>(p.c_str()), NULL}; |
| 92 | |
| 93 | // FTS_NOCHDIR - Avoid changing cwd, which could cause unexpected behavior |
| 94 | // in multithreaded programs |
| 95 | // FTS_PHYSICAL - Don't follow symlinks. Prevents deletion of files outside |
| 96 | // of the specified directory |
| 97 | // FTS_XDEV - Don't cross filesystem boundaries |
| 98 | ftsp = fts_open(files, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL); |
| 99 | if (!ftsp) { |
| 100 | return; |
| 101 | } |
| 102 | |
| 103 | while ((curr = fts_read(ftsp))) { |
| 104 | switch (curr->fts_info) { |
| 105 | case FTS_NS: |
| 106 | case FTS_DNR: |
| 107 | case FTS_ERR: |
| 108 | LOG(WARNING) << "Can't read " << curr->fts_accpath; |
| 109 | break; |
| 110 | |
| 111 | case FTS_DC: |
| 112 | case FTS_DOT: |
| 113 | case FTS_NSOK: |
| 114 | // Not reached unless FTS_LOGICAL, FTS_SEEDOT, or FTS_NOSTAT were |
| 115 | // passed to fts_open() |
| 116 | break; |
| 117 | |
| 118 | case FTS_D: |
| 119 | // Do nothing. Need depth-first search, so directories are deleted |
| 120 | // in FTS_DP |
| 121 | break; |
| 122 | |
| 123 | case FTS_DP: |
| 124 | case FTS_F: |
| 125 | case FTS_SL: |
| 126 | case FTS_SLNONE: |
| 127 | case FTS_DEFAULT: |
| 128 | VLOG(1) << "Removing " << curr->fts_path; |
| 129 | if (remove(curr->fts_accpath) < 0) { |
| 130 | LOG(WARNING) << curr->fts_path |
| 131 | << ": Failed to remove: " << strerror(curr->fts_errno); |
| 132 | } |
| 133 | break; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | if (ftsp) { |
| 138 | fts_close(ftsp); |
| 139 | } |
| 140 | } |
| 141 | |
Brian Silverman | 61175fb | 2016-03-13 15:35:56 -0400 | [diff] [blame] | 142 | } // namespace util |
| 143 | } // namespace aos |