blob: 317206ef65f13c17062bb62c143a81c3f7d4965f [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#include "aos/util/file.h"
Brian Silverman61175fb2016-03-13 15:35:56 -04002
3#include <fcntl.h>
Austin Schuhe991fe22020-11-18 16:53:39 -08004#include <fts.h>
davidjevans8b9b52f2021-09-17 08:57:30 -07005#include <sys/mman.h>
Austin Schuhfccb2d02020-01-26 16:11:19 -08006#include <sys/stat.h>
7#include <sys/types.h>
Brian Silverman61175fb2016-03-13 15:35:56 -04008#include <unistd.h>
9
James Kuszmaul3ae42262019-11-08 12:33:41 -080010#include <string_view>
Austin Schuhe2df81b2021-08-16 10:52:27 -070011#if __has_feature(memory_sanitizer)
12#include <sanitizer/msan_interface.h>
13#endif
James Kuszmaul3ae42262019-11-08 12:33:41 -080014
John Park33858a32018-09-28 23:05:48 -070015#include "aos/scoped/scoped_fd.h"
Brian Silverman61175fb2016-03-13 15:35:56 -040016
17namespace aos {
18namespace util {
19
James Kuszmaul3ae42262019-11-08 12:33:41 -080020::std::string ReadFileToStringOrDie(const std::string_view filename) {
Brian Silverman61175fb2016-03-13 15:35:56 -040021 ::std::string r;
Austin Schuhcb108412019-10-13 16:09:54 -070022 ScopedFD fd(open(::std::string(filename).c_str(), O_RDONLY));
Alex Perrycb7da4b2019-08-28 19:35:56 -070023 PCHECK(fd.get() != -1) << ": opening " << filename;
Brian Silverman61175fb2016-03-13 15:35:56 -040024 while (true) {
25 char buffer[1024];
26 const ssize_t result = read(fd.get(), buffer, sizeof(buffer));
Alex Perrycb7da4b2019-08-28 19:35:56 -070027 PCHECK(result >= 0) << ": reading from " << filename;
28 if (result == 0) {
Brian Silverman61175fb2016-03-13 15:35:56 -040029 break;
30 }
31 r.append(buffer, result);
32 }
33 return r;
34}
35
James Kuszmaul3ae42262019-11-08 12:33:41 -080036void WriteStringToFileOrDie(const std::string_view filename,
Austin Schuhe3fc0532021-02-07 22:14:22 -080037 const std::string_view contents,
38 mode_t permissions) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070039 ::std::string r;
40 ScopedFD fd(open(::std::string(filename).c_str(),
Austin Schuhe3fc0532021-02-07 22:14:22 -080041 O_CREAT | O_WRONLY | O_TRUNC, permissions));
Alex Perrycb7da4b2019-08-28 19:35:56 -070042 PCHECK(fd.get() != -1) << ": opening " << filename;
43 size_t size_written = 0;
44 while (size_written != contents.size()) {
45 const ssize_t result = write(fd.get(), contents.data() + size_written,
46 contents.size() - size_written);
47 PCHECK(result >= 0) << ": reading from " << filename;
48 if (result == 0) {
49 break;
50 }
51
52 size_written += result;
53 }
54}
55
Brian Silvermana9f2ec92020-10-06 18:00:53 -070056bool MkdirPIfSpace(std::string_view path, mode_t mode) {
Austin Schuhfccb2d02020-01-26 16:11:19 -080057 auto last_slash_pos = path.find_last_of("/");
58
59 std::string folder(last_slash_pos == std::string_view::npos
60 ? std::string_view("")
61 : path.substr(0, last_slash_pos));
Brian Silvermana9f2ec92020-10-06 18:00:53 -070062 if (folder.empty()) {
63 return true;
64 }
65 if (!MkdirPIfSpace(folder, mode)) {
66 return false;
67 }
Austin Schuhfccb2d02020-01-26 16:11:19 -080068 const int result = mkdir(folder.c_str(), mode);
69 if (result == -1 && errno == EEXIST) {
70 VLOG(2) << folder << " already exists";
Brian Silvermana9f2ec92020-10-06 18:00:53 -070071 return true;
72 } else if (result == -1 && errno == ENOSPC) {
73 VLOG(2) << "Out of space";
74 return false;
Austin Schuhfccb2d02020-01-26 16:11:19 -080075 } else {
76 VLOG(1) << "Created " << folder;
77 }
78 PCHECK(result == 0) << ": Error creating " << folder;
Brian Silvermana9f2ec92020-10-06 18:00:53 -070079 return true;
Austin Schuhfccb2d02020-01-26 16:11:19 -080080}
81
James Kuszmaulf8178092020-05-10 18:46:45 -070082bool PathExists(std::string_view path) {
83 struct stat buffer;
84 return stat(path.data(), &buffer) == 0;
85}
86
Austin Schuhe991fe22020-11-18 16:53:39 -080087void UnlinkRecursive(std::string_view path) {
88 FTS *ftsp = NULL;
89 FTSENT *curr;
90
91 // Cast needed (in C) because fts_open() takes a "char * const *", instead
92 // of a "const char * const *", which is only allowed in C++. fts_open()
93 // does not modify the argument.
94 std::string p(path);
95 char *files[] = {const_cast<char *>(p.c_str()), NULL};
96
97 // FTS_NOCHDIR - Avoid changing cwd, which could cause unexpected behavior
98 // in multithreaded programs
99 // FTS_PHYSICAL - Don't follow symlinks. Prevents deletion of files outside
100 // of the specified directory
101 // FTS_XDEV - Don't cross filesystem boundaries
102 ftsp = fts_open(files, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL);
103 if (!ftsp) {
104 return;
105 }
106
107 while ((curr = fts_read(ftsp))) {
Austin Schuhe2df81b2021-08-16 10:52:27 -0700108#if __has_feature(memory_sanitizer)
109 // fts_read doesn't have propper msan interceptors. Unpoison it ourselves.
110 if (curr) {
111 __msan_unpoison(curr, sizeof(*curr));
112 __msan_unpoison_string(curr->fts_accpath);
113 __msan_unpoison_string(curr->fts_path);
114 __msan_unpoison_string(curr->fts_name);
115 }
116#endif
Austin Schuhe991fe22020-11-18 16:53:39 -0800117 switch (curr->fts_info) {
118 case FTS_NS:
119 case FTS_DNR:
120 case FTS_ERR:
121 LOG(WARNING) << "Can't read " << curr->fts_accpath;
122 break;
123
124 case FTS_DC:
125 case FTS_DOT:
126 case FTS_NSOK:
127 // Not reached unless FTS_LOGICAL, FTS_SEEDOT, or FTS_NOSTAT were
128 // passed to fts_open()
129 break;
130
131 case FTS_D:
132 // Do nothing. Need depth-first search, so directories are deleted
133 // in FTS_DP
134 break;
135
136 case FTS_DP:
137 case FTS_F:
138 case FTS_SL:
139 case FTS_SLNONE:
140 case FTS_DEFAULT:
141 VLOG(1) << "Removing " << curr->fts_path;
142 if (remove(curr->fts_accpath) < 0) {
143 LOG(WARNING) << curr->fts_path
144 << ": Failed to remove: " << strerror(curr->fts_errno);
145 }
146 break;
147 }
148 }
149
150 if (ftsp) {
151 fts_close(ftsp);
152 }
153}
154
Austin Schuhe4d1a682021-10-01 15:04:50 -0700155std::shared_ptr<absl::Span<uint8_t>> MMapFile(const std::string &path,
156 FileOptions options) {
157 int fd =
158 open(path.c_str(), options == FileOptions::kReadable ? O_RDONLY : O_RDWR);
davidjevans8b9b52f2021-09-17 08:57:30 -0700159 PCHECK(fd != -1) << "Unable to open file " << path;
160 struct stat sb;
161 PCHECK(fstat(fd, &sb) != -1) << ": Unable to get file size of " << path;
Austin Schuhe4d1a682021-10-01 15:04:50 -0700162 uint8_t *start = reinterpret_cast<uint8_t *>(mmap(
163 NULL, sb.st_size,
164 options == FileOptions::kReadable ? PROT_READ : (PROT_READ | PROT_WRITE),
165 MAP_SHARED, fd, 0));
davidjevans8b9b52f2021-09-17 08:57:30 -0700166 CHECK(start != MAP_FAILED) << ": Unable to open mapping to file " << path;
167 std::shared_ptr<absl::Span<uint8_t>> span =
168 std::shared_ptr<absl::Span<uint8_t>>(
169 new absl::Span<uint8_t>(start, sb.st_size),
170 [](absl::Span<uint8_t> *span) {
Austin Schuhe4d1a682021-10-01 15:04:50 -0700171 PCHECK(msync(span->data(), span->size(), MS_SYNC) == 0)
172 << ": Failed to flush data before unmapping.";
davidjevans8b9b52f2021-09-17 08:57:30 -0700173 PCHECK(munmap(span->data(), span->size()) != -1);
174 delete span;
175 });
176 close(fd);
177 return span;
178}
179
Brian Silverman61175fb2016-03-13 15:35:56 -0400180} // namespace util
181} // namespace aos