blob: 4473d4dc85f6e66969de0de222868dea35f135eb [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>
Austin Schuhfccb2d02020-01-26 16:11:19 -08005#include <sys/stat.h>
6#include <sys/types.h>
Brian Silverman61175fb2016-03-13 15:35:56 -04007#include <unistd.h>
8
James Kuszmaul3ae42262019-11-08 12:33:41 -08009#include <string_view>
Austin Schuhe2df81b2021-08-16 10:52:27 -070010#if __has_feature(memory_sanitizer)
11#include <sanitizer/msan_interface.h>
12#endif
James Kuszmaul3ae42262019-11-08 12:33:41 -080013
John Park33858a32018-09-28 23:05:48 -070014#include "aos/scoped/scoped_fd.h"
Brian Silverman61175fb2016-03-13 15:35:56 -040015
16namespace aos {
17namespace util {
18
James Kuszmaul3ae42262019-11-08 12:33:41 -080019::std::string ReadFileToStringOrDie(const std::string_view filename) {
Brian Silverman61175fb2016-03-13 15:35:56 -040020 ::std::string r;
Austin Schuhcb108412019-10-13 16:09:54 -070021 ScopedFD fd(open(::std::string(filename).c_str(), O_RDONLY));
Alex Perrycb7da4b2019-08-28 19:35:56 -070022 PCHECK(fd.get() != -1) << ": opening " << filename;
Brian Silverman61175fb2016-03-13 15:35:56 -040023 while (true) {
24 char buffer[1024];
25 const ssize_t result = read(fd.get(), buffer, sizeof(buffer));
Alex Perrycb7da4b2019-08-28 19:35:56 -070026 PCHECK(result >= 0) << ": reading from " << filename;
27 if (result == 0) {
Brian Silverman61175fb2016-03-13 15:35:56 -040028 break;
29 }
30 r.append(buffer, result);
31 }
32 return r;
33}
34
James Kuszmaul3ae42262019-11-08 12:33:41 -080035void WriteStringToFileOrDie(const std::string_view filename,
Austin Schuhe3fc0532021-02-07 22:14:22 -080036 const std::string_view contents,
37 mode_t permissions) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070038 ::std::string r;
39 ScopedFD fd(open(::std::string(filename).c_str(),
Austin Schuhe3fc0532021-02-07 22:14:22 -080040 O_CREAT | O_WRONLY | O_TRUNC, permissions));
Alex Perrycb7da4b2019-08-28 19:35:56 -070041 PCHECK(fd.get() != -1) << ": opening " << filename;
42 size_t size_written = 0;
43 while (size_written != contents.size()) {
44 const ssize_t result = write(fd.get(), contents.data() + size_written,
45 contents.size() - size_written);
46 PCHECK(result >= 0) << ": reading from " << filename;
47 if (result == 0) {
48 break;
49 }
50
51 size_written += result;
52 }
53}
54
Brian Silvermana9f2ec92020-10-06 18:00:53 -070055bool MkdirPIfSpace(std::string_view path, mode_t mode) {
Austin Schuhfccb2d02020-01-26 16:11:19 -080056 auto last_slash_pos = path.find_last_of("/");
57
58 std::string folder(last_slash_pos == std::string_view::npos
59 ? std::string_view("")
60 : path.substr(0, last_slash_pos));
Brian Silvermana9f2ec92020-10-06 18:00:53 -070061 if (folder.empty()) {
62 return true;
63 }
64 if (!MkdirPIfSpace(folder, mode)) {
65 return false;
66 }
Austin Schuhfccb2d02020-01-26 16:11:19 -080067 const int result = mkdir(folder.c_str(), mode);
68 if (result == -1 && errno == EEXIST) {
69 VLOG(2) << folder << " already exists";
Brian Silvermana9f2ec92020-10-06 18:00:53 -070070 return true;
71 } else if (result == -1 && errno == ENOSPC) {
72 VLOG(2) << "Out of space";
73 return false;
Austin Schuhfccb2d02020-01-26 16:11:19 -080074 } else {
75 VLOG(1) << "Created " << folder;
76 }
77 PCHECK(result == 0) << ": Error creating " << folder;
Brian Silvermana9f2ec92020-10-06 18:00:53 -070078 return true;
Austin Schuhfccb2d02020-01-26 16:11:19 -080079}
80
James Kuszmaulf8178092020-05-10 18:46:45 -070081bool PathExists(std::string_view path) {
82 struct stat buffer;
83 return stat(path.data(), &buffer) == 0;
84}
85
Austin Schuhe991fe22020-11-18 16:53:39 -080086void UnlinkRecursive(std::string_view path) {
87 FTS *ftsp = NULL;
88 FTSENT *curr;
89
90 // Cast needed (in C) because fts_open() takes a "char * const *", instead
91 // of a "const char * const *", which is only allowed in C++. fts_open()
92 // does not modify the argument.
93 std::string p(path);
94 char *files[] = {const_cast<char *>(p.c_str()), NULL};
95
96 // FTS_NOCHDIR - Avoid changing cwd, which could cause unexpected behavior
97 // in multithreaded programs
98 // FTS_PHYSICAL - Don't follow symlinks. Prevents deletion of files outside
99 // of the specified directory
100 // FTS_XDEV - Don't cross filesystem boundaries
101 ftsp = fts_open(files, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL);
102 if (!ftsp) {
103 return;
104 }
105
106 while ((curr = fts_read(ftsp))) {
Austin Schuhe2df81b2021-08-16 10:52:27 -0700107#if __has_feature(memory_sanitizer)
108 // fts_read doesn't have propper msan interceptors. Unpoison it ourselves.
109 if (curr) {
110 __msan_unpoison(curr, sizeof(*curr));
111 __msan_unpoison_string(curr->fts_accpath);
112 __msan_unpoison_string(curr->fts_path);
113 __msan_unpoison_string(curr->fts_name);
114 }
115#endif
Austin Schuhe991fe22020-11-18 16:53:39 -0800116 switch (curr->fts_info) {
117 case FTS_NS:
118 case FTS_DNR:
119 case FTS_ERR:
120 LOG(WARNING) << "Can't read " << curr->fts_accpath;
121 break;
122
123 case FTS_DC:
124 case FTS_DOT:
125 case FTS_NSOK:
126 // Not reached unless FTS_LOGICAL, FTS_SEEDOT, or FTS_NOSTAT were
127 // passed to fts_open()
128 break;
129
130 case FTS_D:
131 // Do nothing. Need depth-first search, so directories are deleted
132 // in FTS_DP
133 break;
134
135 case FTS_DP:
136 case FTS_F:
137 case FTS_SL:
138 case FTS_SLNONE:
139 case FTS_DEFAULT:
140 VLOG(1) << "Removing " << curr->fts_path;
141 if (remove(curr->fts_accpath) < 0) {
142 LOG(WARNING) << curr->fts_path
143 << ": Failed to remove: " << strerror(curr->fts_errno);
144 }
145 break;
146 }
147 }
148
149 if (ftsp) {
150 fts_close(ftsp);
151 }
152}
153
Brian Silverman61175fb2016-03-13 15:35:56 -0400154} // namespace util
155} // namespace aos