Line data Source code
1 : #pragma once
2 :
3 : #include <bitset>
4 : #include <cstdint>
5 : #include <memory>
6 : #include <string>
7 :
8 : #include "envoy/api/io_error.h"
9 : #include "envoy/common/platform.h"
10 : #include "envoy/common/pure.h"
11 : #include "envoy/common/time.h"
12 :
13 : #include "absl/status/statusor.h"
14 : #include "absl/strings/string_view.h"
15 : #include "absl/types/optional.h"
16 :
17 : namespace Envoy {
18 : namespace Filesystem {
19 :
20 : using FlagSet = std::bitset<5>;
21 :
22 : enum class DestinationType { File, Stderr, Stdout, TmpFile };
23 :
24 : enum class FileType { Regular, Directory, Other };
25 :
26 : struct FileInfo {
27 : const std::string name_;
28 : // the size of the file in bytes, or `nullopt` if the size could not be determined
29 : // (e.g. for directories, or windows symlinks.)
30 : const absl::optional<uint64_t> size_;
31 : // Note that if the file represented by name_ is a symlink, type_ will be the file type of the
32 : // target. For example, if name_ is a symlink to a directory, its file type will be Directory.
33 : // A broken symlink on posix will have `FileType::Regular`.
34 : const FileType file_type_;
35 : const absl::optional<SystemTime> time_created_;
36 : const absl::optional<SystemTime> time_last_accessed_;
37 : const absl::optional<SystemTime> time_last_modified_;
38 : };
39 :
40 : /**
41 : * Abstraction for a basic file on disk.
42 : */
43 : class File {
44 : public:
45 659 : virtual ~File() = default;
46 :
47 : enum Operation {
48 : // Open a file for reading.
49 : Read,
50 : // Open a file for writing. The file will be truncated if neither Append nor
51 : // KeepExisting is set.
52 : Write,
53 : // Create the file if it does not already exist
54 : Create,
55 : // If writing, append to the file rather than writing to the beginning and
56 : // truncating after write.
57 : Append,
58 : // To open for write, without appending, and without truncating, add the
59 : // KeepExistingData flag. It is especially important to set this flag if
60 : // using pwrite, as the Windows implementation of truncation will interact
61 : // poorly with pwrite.
62 : KeepExistingData,
63 : };
64 :
65 : /**
66 : * Open the file with Flag
67 : * The file will be closed when this object is destructed
68 : *
69 : * @return bool whether the open succeeded
70 : */
71 : virtual Api::IoCallBoolResult open(FlagSet flags) PURE;
72 :
73 : /**
74 : * Write the buffer to the file. The file must be explicitly opened before writing.
75 : *
76 : * @return ssize_t number of bytes written, or -1 for failure
77 : */
78 : virtual Api::IoCallSizeResult write(absl::string_view buffer) PURE;
79 :
80 : /**
81 : * Get additional details about the file. May or may not require a file system operation.
82 : *
83 : * @return FileInfo the file info.
84 : */
85 : virtual Api::IoCallResult<FileInfo> info() PURE;
86 :
87 : /**
88 : * Close the file.
89 : *
90 : * @return bool whether the close succeeded
91 : */
92 : virtual Api::IoCallBoolResult close() PURE;
93 :
94 : /**
95 : * Read a chunk of data from the file to a buffer. The file must be explicitly opened
96 : * before reading.
97 : * @param buf The buffer to copy the data into.
98 : * @param count The maximum number of bytes to read.
99 : * @param offset The offset in the file at which to start reading.
100 : * @return ssize_t number of bytes read, or -1 for failure.
101 : */
102 : virtual Api::IoCallSizeResult pread(void* buf, uint64_t count, uint64_t offset) PURE;
103 :
104 : /**
105 : * Write a chunk of data from a buffer to the file. The file must be explicitly opened
106 : * before writing.
107 : * @param buf The buffer to read the data from.
108 : * @param count The maximum number of bytes to write.
109 : * @param offset The offset in the file at which to start writing.
110 : * @return ssize_t number of bytes written, or -1 for failure.
111 : */
112 : virtual Api::IoCallSizeResult pwrite(const void* buf, uint64_t count, uint64_t offset) PURE;
113 :
114 : /**
115 : * @return bool is the file open
116 : */
117 : virtual bool isOpen() const PURE;
118 :
119 : /**
120 : * @return string the file path
121 : */
122 : virtual std::string path() const PURE;
123 :
124 : /**
125 : * @return the type of the destination
126 : */
127 : virtual DestinationType destinationType() const PURE;
128 : };
129 :
130 : using FilePtr = std::unique_ptr<File>;
131 :
132 : /**
133 : * Contains the result of splitting the file name and its parent directory from
134 : * a given file path.
135 : */
136 : struct PathSplitResult {
137 : absl::string_view directory_;
138 : absl::string_view file_;
139 : };
140 :
141 : /**
142 : * Contains the file type and the path.
143 : */
144 : struct FilePathAndType {
145 0 : bool operator==(const FilePathAndType& rhs) const {
146 0 : return file_type_ == rhs.file_type_ && path_ == rhs.path_;
147 0 : }
148 : FilePathAndType() = default;
149 : FilePathAndType(DestinationType file_type, absl::string_view path)
150 659 : : file_type_(file_type), path_(path) {}
151 : DestinationType file_type_;
152 : std::string path_;
153 : };
154 :
155 : /**
156 : * Abstraction for some basic filesystem operations
157 : */
158 : class Instance {
159 : public:
160 150 : virtual ~Instance() = default;
161 :
162 : /**
163 : * @param file_info The path and the type of the File
164 : * @return a FilePtr. The file is not opened.
165 : */
166 : virtual FilePtr createFile(const FilePathAndType& file_info) PURE;
167 :
168 : /**
169 : * @return bool whether a file exists on disk and can be opened for read.
170 : */
171 : virtual bool fileExists(const std::string& path) PURE;
172 :
173 : /**
174 : * @return FileInfo containing information about the file, or an error status.
175 : */
176 : virtual Api::IoCallResult<FileInfo> stat(absl::string_view path) PURE;
177 :
178 : /**
179 : * Attempts to create the given path, recursively if necessary.
180 : * @return bool true if one or more directories was created and the path exists,
181 : * false if the path already existed, an error status if the path does
182 : * not exist after the call.
183 : */
184 : virtual Api::IoCallBoolResult createPath(absl::string_view path) PURE;
185 :
186 : /**
187 : * @return bool whether a directory exists on disk and can be opened for read.
188 : */
189 : virtual bool directoryExists(const std::string& path) PURE;
190 :
191 : /**
192 : * @return ssize_t the size in bytes of the specified file, or -1 if the file size
193 : * cannot be determined for any reason, including without limitation
194 : * the non-existence of the file.
195 : */
196 : virtual ssize_t fileSize(const std::string& path) PURE;
197 :
198 : /**
199 : * @return full file content as a string or an error if the file can not be read.
200 : * Be aware, this is not most highly performing file reading method.
201 : */
202 : virtual absl::StatusOr<std::string> fileReadToEnd(const std::string& path) PURE;
203 :
204 : /**
205 : * @path file path to split
206 : * @return PathSplitResult containing the parent directory of the input path and the file name or
207 : * an error status.
208 : */
209 : virtual absl::StatusOr<PathSplitResult> splitPathFromFilename(absl::string_view path) PURE;
210 :
211 : /**
212 : * Determine if the path is on a list of paths Envoy will refuse to access. This
213 : * is a basic sanity check for users, denying some clearly bad paths. Paths
214 : * may still be problematic (e.g. indirectly leading to /dev/mem) even if this
215 : * returns false, it is up to the user to validate that supplied paths are
216 : * valid.
217 : * @param path some filesystem path.
218 : * @return is the path on the deny list?
219 : */
220 : virtual bool illegalPath(const std::string& path) PURE;
221 : };
222 :
223 : using InstancePtr = std::unique_ptr<Instance>;
224 :
225 : struct DirectoryEntry {
226 : // name_ is the name of the file in the directory, not including the directory path itself
227 : // For example, if we have directory a/b containing file c, name_ will be c
228 : std::string name_;
229 :
230 : // Note that if the file represented by name_ is a symlink, type_ will be the file type of the
231 : // target. For example, if name_ is a symlink to a directory, its file type will be Directory.
232 : FileType type_;
233 :
234 : // The file size in bytes for regular files. nullopt for FileType::Directory and FileType::Other,
235 : // and, on Windows, also nullopt for symlinks, and on Linux nullopt for broken symlinks.
236 : absl::optional<uint64_t> size_bytes_;
237 :
238 148 : bool operator==(const DirectoryEntry& rhs) const {
239 148 : return name_ == rhs.name_ && type_ == rhs.type_ && size_bytes_ == rhs.size_bytes_;
240 148 : }
241 : };
242 :
243 : class DirectoryIteratorImpl;
244 :
245 : // Failures during this iteration will be silent; check status() after initialization
246 : // and after each increment, if error-handling is desired.
247 : class DirectoryIterator {
248 : public:
249 74 : DirectoryIterator() : entry_({"", FileType::Other, absl::nullopt}) {}
250 74 : virtual ~DirectoryIterator() = default;
251 :
252 259 : const DirectoryEntry& operator*() const { return entry_; }
253 :
254 148 : bool operator!=(const DirectoryIterator& rhs) const { return !(entry_ == *rhs); }
255 :
256 : virtual DirectoryIteratorImpl& operator++() PURE;
257 :
258 0 : const absl::Status& status() const { return status_; }
259 :
260 : protected:
261 : DirectoryEntry entry_;
262 : absl::Status status_;
263 : };
264 :
265 : } // namespace Filesystem
266 : } // namespace Envoy
|